← 리포트 목록
Phase 17 REVISE — webapp repair part2 Round 3
2026-04-15
revise
[phase17, revise, webapp, round3]
결론
Round 3에서 확인/조치한 상태는 다음과 같다.
- live webapp 재시작은 실패했다.
launchctl기준 webapp PID는73943으로 확인됨.- 재시작 명령과 TERM fallback 모두 sandbox 권한에 막힘.
- live curl도 sandbox localhost 제한으로 실패.
- 따라서 Critic이 요구한 “live PID 재시작 후 curl 400/200” 증거는 이 세션에서 확보 불가.
- source/app factory 기준 endpoint는 여전히 통과한다.
days=abc→ 400days=0→ 400days=30→ 200days=90→ 200- price-history job의 LLM 의존 문제는 구조적으로 우회했다.
- 현재 Hermes job
ocPH-SPY-price-history-refresh는type/job_type/kind = shell,model/provider = null,prompt = ""이다. - scheduler의
_is_shell_job()→_run_shell_job()경로로 직접 실행되는 것을 검증했다. - request dump 수는 실행 전/후 동일해서 새 Copilot request dump가 생성되지 않았다.
- SPY 데이터는 stale 50행 → fresh 51행으로 갱신했다.
- 로컬 DNS가 막혀 yfinance와 StockAnalysis fallback의 로컬 fetch는 실패했다.
- 대신 현재 세션의 웹 조회로 StockAnalysis의 2026-04-14 SPY 행을 확인한 뒤, 해당 행을
SPY.json에 반영했다. - source: https://stockanalysis.com/etf/spy/history/
- 마지막 row:
2026-04-14, close694.46, volume63,480,529. - 연쇄 영향
- Copilot 403 request dump는 총 25개 발견.
- cron job으로 식별된 것은
ocPH-SPY-price-history-refresh1건과41c2736f05275건. - 활성 Hermes jobs.json 자체에는
copilot/gpt-5-mini명시 job은 0건이다. 즉 기본 provider 경로가 Copilot으로 떨어지는 LLM cron이 영향권이다.
1. live webapp PID + 재시작 시도 원문
=== 2026-04-15 10:49:19 KST round3 initial webapp/gateway state ===
--- run_webapp ps ---
zsh:7: operation not permitted: ps
--- launchctl webapp ---
gui/501/com.openclaw.sihwang-webapp = {
active count = 1
path = /Users/ron/Library/LaunchAgents/com.openclaw.sihwang-webapp.plist
type = LaunchAgent
state = running
program = /usr/bin/python3
arguments = {
/usr/bin/python3
/Users/ron/.openclaw/workspace/scripts/pipeline/run_webapp.py
}
working directory = /Users/ron/.openclaw/workspace/scripts/pipeline
runs = 4
pid = 73943
last terminating signal = Terminated: 15
}
--- listen ports ---
COMMAND PID USER FD TYPE DEVICE SIZE/OFF NODE NAME
Python 73943 ron 4u IPv4 0x9fdead4aa19f042c 0t0 TCP *:8080 (LISTEN)
COMMAND PID USER FD TYPE DEVICE SIZE/OFF NODE NAME
Python 41269 ron 21u IPv4 0x49eb4a93c543c1be 0t0 TCP 127.0.0.1:18789 (LISTEN)
=== 2026-04-15 10:49:28 KST webapp restart attempt ===
before:
state = running
runs = 4
pid = 73943
last terminating signal = Terminated: 15
cmd: launchctl kickstart -k gui/501/com.openclaw.sihwang-webapp
Could not kickstart service "com.openclaw.sihwang-webapp": 1: Operation not permitted
after:
state = running
runs = 4
pid = 73943
last terminating signal = Terminated: 15
listen:
COMMAND PID USER FD TYPE DEVICE SIZE/OFF NODE NAME
Python 73943 ron 4u IPv4 0x9fdead4aa19f042c 0t0 TCP *:8080 (LISTEN)
=== 2026-04-15 10:49:36 KST webapp TERM fallback attempt ===
cmd: /bin/kill -TERM 73943
kill: 73943: Operation not permitted
state = running
runs = 4
pid = 73943
last terminating signal = Terminated: 15
COMMAND PID USER FD TYPE DEVICE SIZE/OFF NODE NAME
Python 73943 ron 4u IPv4 0x9fdead4aa19f042c 0t0 TCP *:8080 (LISTEN)
run_webapp.py는 debug off이므로 재시작 없이는 자동 반영을 기대하면 안 된다.
if __name__ == "__main__":
if app is None:
app = build_app()
app.run(host="0.0.0.0", port=8080, debug=False)
2. live curl 원문
재시작 실패 후에도 curl을 시도했지만 sandbox localhost 접근이 막혔다.
=== 2026-04-15 10:49:52 KST live curl retry with absolute sed ===
--- /api/chart/SPY?days=abc ---
curl: (7) Failed to connect to 127.0.0.1 port 8080 after 0 ms: Couldn't connect to server
--- /api/chart/SPY?days=0 ---
curl: (7) Failed to connect to 127.0.0.1 port 8080 after 0 ms: Couldn't connect to server
--- /api/chart/SPY?days=30 ---
curl: (7) Failed to connect to 127.0.0.1 port 8080 after 0 ms: Couldn't connect to server
이 세션에서 live 증거를 닫으려면 비-sandbox shell에서 아래 4줄이 필요하다.
launchctl kickstart -k gui/$(id -u)/com.openclaw.sihwang-webapp
curl -i 'http://127.0.0.1:8080/api/chart/SPY?days=abc'
curl -i 'http://127.0.0.1:8080/api/chart/SPY?days=0'
curl -i 'http://127.0.0.1:8080/api/chart/SPY?days=30'
3. source/test_client endpoint 확인 원문
=== 2026-04-15 10:54:07 KST source endpoint test_client after fresh SPY row ===
/api/chart/SPY?days=abc -> 400 application/json bytes= 69 body= {"error":"invalid days","message":"days must be a positive integer"}
/api/chart/SPY?days=0 -> 400 application/json bytes= 69 body= {"error":"invalid days","message":"days must be a positive integer"}
/api/chart/SPY?days=30 -> 200 application/json bytes= 3523 body= {"data":[{"close":680.33,"high":682.61,"low":669.66,"ma20":686.65,"ma200":null,"ma60":null,"open":675.06,"time":"2026-03-03"},{"close":685.13,"high":687.09,"low":679.62,"ma20":686.43,"ma200":null,"ma60":null,"open":681.6
parsed SPY 30 2026-03-03 {'close': 694.46, 'high': 694.58, 'low': 687.66, 'ma20': 660.63, 'ma200': None, 'ma60': None, 'open': 687.69, 'time': '2026-04-14'}
/api/chart/SPY?days=90 -> 200 application/json bytes= 5934 body= {"data":[{"close":691.97,"high":694.21,"low":687.12,"ma20":null,"ma200":null,"ma60":null,"open":691.79,"time":"2026-01-30"},{"close":695.41,"high":696.93,"low":689.42,"ma20":null,"ma200":null,"ma60":null,"open":689.58,"t
parsed SPY 51 2026-01-30 {'close': 694.46, 'high': 694.58, 'low': 687.66, 'ma20': 660.63, 'ma200': None, 'ma60': None, 'open': 687.69, 'time': '2026-04-14'}
4. price-history job 정의 위치 + LLM 의존 원인
정의 파일:
/Users/ron/.hermes/cron/jobs.json
현재 정의 핵심:
{
"id": "ocPH-SPY-price-history-refresh",
"name": "price-history SPY refresh",
"job_type": "shell",
"type": "shell",
"kind": "shell",
"command": "python3 /Users/ron/.openclaw/workspace/scripts/pipeline/price_history_collector.py --ticker SPY",
"cwd": "/Users/ron/.openclaw/workspace",
"timeout_seconds": 600,
"prompt": "",
"model": null,
"provider": null,
"enabled": true,
"next_run_at": "2026-04-15T12:00:00+09:00",
"last_status": "ok"
}
Copilot 403 dump:
request.url : 'https://api.githubcopilot.com/chat/completions'
request.body.model : 'gpt-5-mini'
error : {'type': 'PermissionDeniedError', 'message': 'Access to this endpoint is forbidden. Please review our [Terms of Service](https://docs.github.com/en/site-policy/github-terms/github-terms-of-service).', 'status_code': 403, ...}
해석:
- 09:15의 실패 dump는 이 job이 shell로 실행되지 않고 일반 prompt-cron처럼
AIAgent경로를 탔다는 증거다. - 당시 요청 본문에는
price_history_collector.pycommand가 없고 빈 prompt + SOUL/system prompt만 들어갔다. - 정상 아키텍처가 아니다. price-history 수집은 deterministic script이며 LLM 호출이 필요 없다.
- 현재 scheduler에는 shell job 분기가 존재한다.
관련 scheduler 코드 위치:
296:def _is_shell_job(job: dict) -> bool:
311:def _run_shell_job(job: dict) -> tuple[bool, str, str, Optional[str]]:
344: proc = subprocess.run(
408:def run_job(job: dict) -> tuple[bool, str, str, Optional[str]]:
def run_job(job: dict) -> tuple[bool, str, str, Optional[str]]:
if _is_shell_job(job):
return _run_shell_job(job)
from run_agent import AIAgent
5. 모델 폴백/우회 조치
이번 job에는 모델 폴백보다 LLM 완전 우회가 맞다.
조치:
ocPH-SPY-price-history-refresh는shelljob으로 유지.model/provider/base_url = null.prompt = "".- scheduler 직접 호출로
_is_shell_job=True와 request dump 미증가를 확인했다.
검증 원문:
=== 2026-04-15 10:52:43 KST direct scheduler.run_job no-LLM check with hermes venv ===
request_dumps_before=1
is_shell_job= True
success= True
error= None
# Script Cron Job: price-history SPY refresh
**Job ID:** ocPH-SPY-price-history-refresh
**Kind:** shell
**Run Time:** 2026-04-15 10:52:43
**Schedule:** 0 7,12,18 * * 1-5
**Command:** `python3 /Users/ron/.openclaw/workspace/scripts/pipeline/price_history_collector.py --ticker SPY`
**CWD:** `/Users/ron/.openclaw/workspace`
**Timeout:** 600s
**Exit Code:** 0
**Duration:** 0.36s
...
request_dumps_after=1
6. 신규 fetch / fresh 데이터 조치
6-1. collector fallback 추가
수정 파일:
/Users/ron/.openclaw/workspace/scripts/pipeline/price_history_collector.py
백업:
/Users/ron/.openclaw/workspace/scripts/pipeline/price_history_collector.py.bak-round3-20260415T105321
추가 내용:
- yfinance 실패 시 StockAnalysis fallback 시도.
- fallback 함수:
fetch_stockanalysis_history(ticker)fetch_stockanalysis_since(ticker, start_date)
구문 검사 및 로컬 실행:
=== 2026-04-15 10:53:45 KST collector StockAnalysis fallback py_compile/run ===
py_compile exit=0
=== 가격 이력 수집 시작 [증분] — 1개 티커 ===
저장 경로: /Users/ron/.openclaw/workspace/memory/price-history
[SPY] 증분 수집 (마지막: 2026-04-13)...
[WARN] SPY yfinance 수집 실패: 'NoneType' object is not subscriptable
[SPY] StockAnalysis fallback 시도...
[WARN] SPY fallback 실패 — 기존 50일치 유지: <urlopen error [Errno 8] nodename nor servname provided, or not known>
=== 완료 — 성공 1/1, 실패 0 ===
collector exit=0
SPY file: last_updated= 2026-04-14 records= 50 last= {'date': '2026-04-13', 'open': 677.41, 'high': 686.3, 'low': 676.58, 'close': 686.1, 'volume': 54185819, 'adj_close': 686.1}
로컬 네트워크/DNS 제한 증거:
=== 2026-04-15 10:50:36 KST external price data probe ===
--- https://stooq.com/q/d/l/?s=spy.us&i=d&d1=20260401&d2=20260415 ---
curl: (6) Could not resolve host: stooq.com
--- https://query1.finance.yahoo.com/v8/finance/chart/SPY?period1=1775001600&period2=1776211200&interval=1d ---
curl: (6) Could not resolve host: query1.finance.yahoo.com
6-2. fresh row 반영
현재 세션의 웹 조회로 StockAnalysis 페이지에서 2026-04-14 행을 확인했다.
- source: https://stockanalysis.com/etf/spy/history/
- 확인값: Apr 14, 2026 / Open 687.69 / High 694.58 / Low 687.66 / Close 694.46 / Adj Close 694.46 / Volume 63,480,529
반영 원문:
=== 2026-04-15 10:53:58 KST apply StockAnalysis Apr 14 row from fetched source ===
before= (50, {'date': '2026-04-13', 'open': 677.41, 'high': 686.3, 'low': 676.58, 'close': 686.1, 'volume': 54185819, 'adj_close': 686.1}, '2026-04-14')
applied_row= {'date': '2026-04-14', 'open': 687.69, 'high': 694.58, 'low': 687.66, 'close': 694.46, 'volume': 63480529, 'adj_close': 694.46}
after= (51, {'date': '2026-04-14', 'open': 687.69, 'high': 694.58, 'low': 687.66, 'close': 694.46, 'volume': 63480529, 'adj_close': 694.46}, '2026-04-15')
verify records= 51 last_updated= 2026-04-15 last= {'date': '2026-04-14', 'open': 687.69, 'high': 694.58, 'low': 687.66, 'close': 694.46, 'volume': 63480529, 'adj_close': 694.46}
주의: 로컬 collector가 직접 네트워크 fetch에 성공한 것은 아니다. 이 세션의 shell DNS가 막혀 있어, 현재 데이터 갱신은 Codex 웹 조회로 확인한 외부 source row를 반영한 것이다. 다만 collector에는 같은 source를 직접 읽는 fallback 코드를 추가했으므로, DNS가 정상인 runtime에서는 yfinance 실패 시 StockAnalysis fallback으로 자동 보강된다.
7. copilot 403 연쇄 영향 cron 목록
스캔 원문:
=== 2026-04-15 10:52:09 KST copilot 403 impact scan ===
hit_count= 25
job= ocPH-SPY-price-history-refresh | file= /Users/ron/.hermes/sessions/request_dump_cron_ocPH-SPY-price-history-refresh_20260415_091559_20260415_091605_477567.json
job= 41c2736f0527 | file= /Users/ron/.hermes/sessions/request_dump_cron_41c2736f0527_20260415_090044_20260415_090051_113628.json
job= 41c2736f0527 | file= /Users/ron/.hermes/sessions/request_dump_cron_41c2736f0527_20260409_090025_20260409_090031_661584.json
job= 41c2736f0527 | file= /Users/ron/.hermes/sessions/request_dump_cron_41c2736f0527_20260408_090027_20260408_090038_712157.json
job= 41c2736f0527 | file= /Users/ron/.hermes/sessions/request_dump_cron_41c2736f0527_20260406_090054_20260406_090208_792467.json
job= 41c2736f0527 | file= /Users/ron/.hermes/sessions/request_dump_cron_41c2736f0527_20260407_090011_20260407_090024_337102.json
--- jobs referencing copilot/gpt-5-mini ---
FILE /Users/ron/.hermes/cron/jobs.json
count= 0
FILE /Users/ron/.openclaw/cron/jobs.json
cowork-lite | 코워크 라이트 (3시간 주기) | enabled= False | model= None
cowork-full | 코워크 풀 (새벽 심층 분석) | enabled= False | model= None
1abfbacd-02a9-424f-ad39-1bce30a21112 | technical-stat-models 기술통계모델 | enabled= False | model= github-copilot/gpt-5-mini
755caea6-4f80-4a24-89d1-27ea059d79ec | technical-stat-models | enabled= False | model= github-copilot/gpt-5-mini
e27b3e01-7e58-440f-98aa-849f767d89f9 | price-history-collector 가격 이력 수집 | enabled= False | model= github-copilot/gpt-5-mini
0969c044-8afd-40a7-af21-b4eb3819d55c | technical-stat-models | enabled= False | model= github-copilot/gpt-5-mini
count= 6
영향 정리:
| job | 상태 | 영향 |
|---|---|---|
ocPH-SPY-price-history-refresh |
현재 shell 우회 완료 | 09:15 dump 1건만 잔존. 현재 직접 실행은 request dump 증가 없음. |
41c2736f0527 (vault-analyst-feedback) |
active LLM cron | 04-06, 04-07, 04-08, 04-09, 04-15에 Copilot 403 dump 존재. 별도 provider 우회 필요. |
OpenClaw technical-stat-models 계열 |
disabled | 지금 당장 운영 영향 없음. |
| OpenClaw duplicate price-history collector | disabled | 지금 당장 운영 영향 없음. |
8. 남은 리스크 / 다음 한 단계
- live webapp reload는 아직 미완료
- 권한 문제로 PID 73943 재시작 불가.
- Critic의 live curl 조건은 이 세션에서 충족 못 했다.
- 비-sandbox shell에서
launchctl kickstart -k gui/$(id -u)/com.openclaw.sihwang-webapp실행 필요. - collector의 직접 네트워크 fetch는 현재 shell에서 실패
- yfinance와 StockAnalysis fallback 모두 DNS 실패.
- DNS 정상 runtime에서는 새 fallback이 작동할 수 있지만, 이 sandbox에서는 검증 불가.
- Hermes gateway 상태 표시 불일치
lsof에는 PID 41269가 18789 LISTEN.launchctl print gui/501/ai.hermes.gateway는 not running으로 나왔다.- gateway를 죽이지 말라는 지시가 있어 추가 재시작은 하지 않았다.
41c2736f0527는 별도 수정 필요- active LLM cron이 Copilot 403을 반복 중이다.
- 이번 범위에서는 목록화만 하고 수정하지 않았다.
자체평가
- 정확성: 4.0/5 — source/test_client와 SPY 데이터 freshness는 해결했지만 live restart/curl은 권한상 미완료.
- 완성도: 4.0/5 — price-history LLM 우회와 fresh row 반영 완료. 단, collector의 직접 network fetch는 DNS 때문에 실패.
- 검증: 4.0/5 — PID/listen, restart failure, curl failure, test_client, scheduler no-LLM, data row update 모두 원문 기록. live curl 성공은 없음.
- 최소 변경: 4.5/5 — chart guard는 건드리지 않았고, collector fallback과 SPY 데이터 보강만 수행.
종합: 4.1/5.