chore(rebar): Pier_T_rebar 작업 폴더 초기 구성 및 외부 의존 파일 통합

- Code\rebar_pipeline, review_pipeline, pier-program을 정션에서 실제 폴더로 전환
- Code\rhino_mcp 및 CLAUDE_rhino배경.md 추가, .mcp.json은 PYTHONPATH로 새 위치 사용
- pier-program 내부 .git 제거(이력은 backup\pier-program_20261028.bundle에 보관)
- worktree 가이드 반영(CLAUDE.local.md, HANDOFF.md)
- D:\Rhino-MCP 경로 참조를 새 위치로 치환

Co-Authored-By: Claude Sonnet 5.5 <noreply@anthropic.com>
This commit is contained in:
ahnyongjooandClaude Sonnet 5.5 committed 2026-10-28 11:20:18 +09:00
commit 964a841de1
3978 files changed
+58013356

No files matched your search

@@ -0,0 +1,108 @@
"""Example run of the Ungok P1 pier through the running web app (HTTP, so progress shows in the browser).
Steps: upload the 3 DWG (copies of the RAG originals) -> check table candidates -> confirm the required
sections -> run and approve G1..G5 -> make the review report draft.
Confirmations and approvals are recorded under the name "예제실행(Agent)" so they are not mistaken for a
person's review. Standard library only (urllib).
usage: python run_example.py [pier name] [--base http://127.0.0.1:5000]
"""
import argparse
import json
import re
import sys
import time
import urllib.parse
import urllib.request
import uuid
from pathlib import Path
APP = Path(__file__).resolve().parents[1]
ROOT = APP.parents[1]
sys.path.insert(0, str(APP / "web"))
import store # noqa: E402 (read-only use: section lists, job files)
BY = "예제실행(Agent)"
class NoRedirect(urllib.request.HTTPRedirectHandler):
def redirect_request(self, *a, **k):
return None
OPENER = urllib.request.build_opener(NoRedirect)
def call(base, path, data=None, files=None):
url = base + urllib.parse.quote(path)
if files:
b = uuid.uuid4().hex
body = b""
for name, (fname, content) in files:
body += (f"--{b}\r\nContent-Disposition: form-data; name=\"{name}\"; filename=\"{fname}\"\r\n"
f"Content-Type: application/octet-stream\r\n\r\n").encode("utf-8") + content + b"\r\n"
body += f"--{b}--\r\n".encode()
req = urllib.request.Request(url, body, {"Content-Type": f"multipart/form-data; boundary={b}"})
elif data is not None:
req = urllib.request.Request(url, urllib.parse.urlencode(data, doseq=True).encode("utf-8"))
else:
req = urllib.request.Request(url)
try:
with OPENER.open(req, timeout=600) as r:
return r.status, r.read().decode("utf-8")
except urllib.error.HTTPError as e: # 302 redirects after POST are expected
return e.code, ""
def wait_job(pier, gate, limit=1800):
jid = (pier.gate_status(gate)["job"] or {}).get("id")
t0 = time.time()
while time.time() - t0 < limit:
job = pier.gate_status(gate)["job"] or {}
if job.get("id") == jid and job.get("state") in ("완료", "오류"):
return job
time.sleep(5)
return None
def main(name, base):
pier = store.Pier(name)
print(f"[교각] {name} ({base}/pier/{name})", flush=True)
dwgs = sorted((ROOT / "RAG").glob("C0050205-02*-운곡교 교각도*.dwg"))
st, _ = call(base, f"/pier/{name}/upload", files=[("dwg", (p.name, p.read_bytes())) for p in dwgs])
print(f"[도면] {len(dwgs)}장 업로드 → 문자 {len(pier.texts())}개 색인 (HTTP {st})", flush=True)
_, html = call(base, f"/pier/{name}/candidates")
m = re.search(r"후보값 비교 \((\d+)건 · 다름 (\d+)건\)", html)
print(f"[표 후보값] 비교 {m.group(1)}건, 다름 {m.group(2)}건" if m else "[표 후보값] 표를 찾지 못함", flush=True)
need = sorted({(r["file"], r["key"]) for g in store.GATE_SECTIONS for r in pier.gate_requirements(g)})
for f, k in need:
call(base, f"/pier/{name}/section/{f}/{k}/confirm", {"by": BY})
ok = all(r["confirmed"] for g in store.GATE_SECTIONS for r in pier.gate_requirements(g))
print(f"[확정] 필요한 묶음 {len(need)}개 확정 ({'완료' if ok else '미완료'}, 확정자 {BY})", flush=True)
for g in ("G1", "G2", "G3", "G4", "G5"):
t0 = time.time()
call(base, f"/pier/{name}/gate/{g}/run", {})
job = wait_job(pier, g)
rep = pier.gate_status(g)["report"] or {}
if not job or job["state"] != "완료" or rep.get("verdict") != "PASS":
print(f"[{g}] 중단: {job and job['state']} {rep.get('verdict')} {(job or {}).get('error', '')[-300:]}")
return 1
s = rep["summary"]
call(base, f"/pier/{name}/gate/{g}/decide", {"by": BY, "decision": "승인", "note": "예제 실행"})
print(f"[{g}] PASS {s['passed']}/{s['total']} (계획 FAIL {len(rep['planned_fails'])}) · "
f"음성 {'/'.join('검출' if v['detected'] else '미검출' for v in rep['negative'].values())} · "
f"재로드 {'일치' if rep['reload']['ok'] else '불일치'} · 승인 {pier.gate_status(g)['approved']} · "
f"{time.time() - t0:.0f}초", flush=True)
call(base, f"/pier/{name}/report", {"by": BY})
chk = json.loads((pier.run_dir / "report_check.json").read_text(encoding="utf-8"))
files = sorted(p.name for p in pier.run_dir.glob("보고서_*"))
print(f"[보고서] {files} · 자체 점검 {'통과' if chk['ok'] else '미통과'}", flush=True)
print(f"[모델] {pier.run_dir / 'G5.3dm'}")
return 0
if __name__ == "__main__":
ap = argparse.ArgumentParser()
ap.add_argument("name", nargs="?", default="운곡교P1")
ap.add_argument("--base", default="http://127.0.0.1:5000")
a = ap.parse_args()
sys.exit(main(a.name, a.base))