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:
commit
964a841de1
3978 files changed
+58013356
No files matched your search
@@ -0,0 +1,162 @@
|
||||
"""Stage B (2026-10-16): confirmation records carry the reason and the app-check status, routes for the app check, the evidence page and the
|
||||
"일치 묶음 일괄 확정" (D4), the report gets the app-check summary. The old 필요한 묶음 일괄 확정 route stays (legacy, stage D decides).
|
||||
Backups of the stage-A state are made by the caller. Refuses to run twice."""
|
||||
from pathlib import Path
|
||||
|
||||
APP = Path(__file__).resolve().parents[2]
|
||||
|
||||
|
||||
def patch(path, pairs):
|
||||
s = path.read_text(encoding="utf-8")
|
||||
assert "# stage B 2026-10-16" not in s, f"{path.name} already patched"
|
||||
for a, b in pairs:
|
||||
assert s.count(a) == 1, (path.name, a[:70])
|
||||
s = s.replace(a, b)
|
||||
path.write_text(s, encoding="utf-8")
|
||||
|
||||
|
||||
STORE = [
|
||||
(''' return {"exists": True, "confirmed": ok, "by": c and c["by"], "at": c and c["at"],
|
||||
"stale": bool(c) and not ok, "info": is_info(key)}''',
|
||||
''' return {"exists": True, "confirmed": ok, "by": c and c["by"], "at": c and c["at"], "why": c and c.get("why"),
|
||||
"app_st": c and c.get("app_st"), "stale": bool(c) and not ok, "info": is_info(key)}'''),
|
||||
(''' def confirm(self, which, key, by):
|
||||
d = self.load(which)
|
||||
conf = self.confirmations()
|
||||
conf[f"{which}:{key}"] = {"sha": sha(d[key]), "by": by, "at": now()}''',
|
||||
''' def confirm(self, which, key, by, why="", app_st=""):
|
||||
"""Human confirmation record (stage B 2026-10-16: + the reason `why` and the app-check status at that time `app_st`)."""
|
||||
d = self.load(which)
|
||||
conf = self.confirmations()
|
||||
rec = {"sha": sha(d[key]), "by": by, "at": now()}
|
||||
if why:
|
||||
rec["why"] = why
|
||||
if app_st:
|
||||
rec["app_st"] = app_st
|
||||
conf[f"{which}:{key}"] = rec'''),
|
||||
]
|
||||
|
||||
APPPY = [
|
||||
("from engine import recommend, reader, report_files, variability # noqa: E402",
|
||||
"from engine import recommend, reader, report_files, variability, verify # noqa: E402"),
|
||||
('''@app.post("/pier/<name>/section/<which>/<key>/confirm")
|
||||
def section_confirm(name, which, key):
|
||||
p = pier_or_404(name)
|
||||
by = request.form.get("by", "").strip()
|
||||
if not by:
|
||||
flash("확정자 이름을 입력하세요.", "fail")
|
||||
else:
|
||||
p.confirm(which, key, by)
|
||||
flash(f"{which}.{key} 확정 ({by})", "ok")
|
||||
return redirect(request.form.get("next") or url_for("pier", name=name))''',
|
||||
'''@app.post("/pier/<name>/section/<which>/<key>/confirm")
|
||||
def section_confirm(name, which, key):
|
||||
"""Confirm one bundle. A bundle the app check does not report as 일치 (주의 / 불일치 / 근거 없음) needs a reason (D3, stage B 2026-10-16)."""
|
||||
p = pier_or_404(name)
|
||||
by = request.form.get("by", "").strip()
|
||||
why = request.form.get("why", "").strip()
|
||||
p.run_autocheck()
|
||||
st = p.section_check(which, key)["st"]
|
||||
if not by:
|
||||
flash("확정자 이름을 입력하세요.", "fail")
|
||||
elif st in ("w", "b", "n") and not why:
|
||||
flash(f"{which}.{key}는 앱 확인이 '{verify.ST_NAME[st]}'입니다. 사유를 입력해야 확정할 수 있습니다.", "fail")
|
||||
else:
|
||||
p.confirm(which, key, by, why, st)
|
||||
flash(f"{which}.{key} 확정 ({by})" + (f" — 앱 확인 {verify.ST_NAME.get(st, st)}, 사유 기록" if why else ""), "ok")
|
||||
return redirect(request.form.get("next") or url_for("pier", name=name))
|
||||
|
||||
|
||||
# ---------------- app check (stage B 2026-10-16)
|
||||
@app.post("/pier/<name>/autocheck")
|
||||
def autocheck_run(name):
|
||||
p = pier_or_404(name)
|
||||
a = p.run_autocheck(force=True)
|
||||
flash(f"앱 확인을 실행했습니다 ({len(a['sections'])}개 묶음). 앱은 확정하지 않습니다.", "ok")
|
||||
return redirect(request.form.get("next") or url_for("pier", name=name))
|
||||
|
||||
|
||||
@app.route("/pier/<name>/evidence/<which>/<key>")
|
||||
def evidence(name, which, key):
|
||||
p = pier_or_404(name)
|
||||
if which not in ("params", "expected") or key not in p.load(which):
|
||||
abort(404)
|
||||
p.run_autocheck()
|
||||
return render_template("evidence.html", p=p, which=which, key=key, chk=p.section_check(which, key), st=p.section_state(which, key),
|
||||
meta=p.section_meta(which, key), grades=variability.GRADES, ST=verify.ST_NAME)
|
||||
|
||||
|
||||
def matched_targets(p):
|
||||
"""Required bundles the app check reports as 일치 and nobody has confirmed (current): the D4 bulk-confirm targets."""
|
||||
p.run_autocheck()
|
||||
out = []
|
||||
for f, k in sorted(required(p)):
|
||||
c, s = p.section_check(f, k), p.section_state(f, k)
|
||||
if c["st"] == "g" and not c["stale"] and not s["confirmed"]:
|
||||
out.append((f, k))
|
||||
return out
|
||||
|
||||
|
||||
def _targets_sha(p, targets):
|
||||
return store.sha([[f, k, p.section_check(f, k)["sha"], p.section_check(f, k)["st"]] for f, k in targets])
|
||||
|
||||
|
||||
@app.route("/pier/<name>/confirm_matched", methods=["GET", "POST"])
|
||||
def confirm_matched(name):
|
||||
"""D4: bulk confirmation of the bundles the app check reports as 일치. The evidence table must have been opened (a marker holds the
|
||||
hash of the target list), a name is required; anything the app does not report as 일치 is not touched."""
|
||||
p = pier_or_404(name)
|
||||
targets = matched_targets(p)
|
||||
if request.method == "POST":
|
||||
by = request.form.get("by", "").strip()
|
||||
mark = store.rjson(p.dir / "confirm_view.json", {})
|
||||
if not targets:
|
||||
flash("확정할 일치 묶음이 없습니다.", "fail")
|
||||
elif mark.get("sha") != _targets_sha(p, targets):
|
||||
flash("근거표를 열어 끝까지 읽은 뒤에 확정할 수 있습니다(대상 목록이 바뀌었으면 다시 여세요).", "fail")
|
||||
elif not by:
|
||||
flash("확정자 이름을 입력하세요.", "fail")
|
||||
else:
|
||||
for f, k in targets:
|
||||
p.confirm(f, k, by, "", "g")
|
||||
flash(f"앱 확인 '일치' 묶음 {len(targets)}개를 {by}이(가) 확정했습니다. 확정 기록에는 입력한 이름만 남습니다.", "ok")
|
||||
return redirect(url_for("pier", name=name))
|
||||
return redirect(url_for("confirm_matched", name=name))
|
||||
store.wjson(p.dir / "confirm_view.json", {"sha": _targets_sha(p, targets), "at": store.now(), "n": len(targets)})
|
||||
rest = []
|
||||
for f, k in sorted(required(p)):
|
||||
if (f, k) in targets:
|
||||
continue
|
||||
s = p.section_state(f, k)
|
||||
if not s["confirmed"]:
|
||||
rest.append(dict(file=f, key=k, chk=p.section_check(f, k)))
|
||||
return render_template("confirm_matched.html", p=p, targets=[dict(file=f, key=k, chk=p.section_check(f, k)) for f, k in targets],
|
||||
rest=rest, ST=verify.ST_NAME)'''),
|
||||
(''' secs = []
|
||||
req = required(p)
|
||||
for s in p.sections():
|
||||
secs.append(dict(s, **p.section_state(s["file"], s["key"]), meta=p.section_meta(s["file"], s["key"]),
|
||||
required=(s["file"], s["key"]) in req))''',
|
||||
''' secs = []
|
||||
req = required(p)
|
||||
p.run_autocheck() # stage B: skipped when nothing changed
|
||||
for s in p.sections():
|
||||
secs.append(dict(s, **p.section_state(s["file"], s["key"]), meta=p.section_meta(s["file"], s["key"]),
|
||||
required=(s["file"], s["key"]) in req, chk=p.section_check(s["file"], s["key"])))'''),
|
||||
(''' origin_same = [s for s in secs if s["required"] and s["meta"]["origin"]] # stage A 2026-10-16''',
|
||||
''' origin_same = [s for s in secs if s["required"] and s["meta"]["origin"]] # stage A 2026-10-16
|
||||
cnt = {k: 0 for k in "gwbnx"} # stage B 2026-10-16
|
||||
for s in secs:
|
||||
if s["required"]:
|
||||
cnt[s["chk"]["st"] if s["chk"]["st"] in cnt else "n"] += 1
|
||||
human = sum(1 for s in secs if s["required"] and s["confirmed"])
|
||||
nmatch = len(matched_targets(p))'''),
|
||||
(''' has_origin=bool(p.info.get("origin")), grades=variability.GRADES,''',
|
||||
''' has_origin=bool(p.info.get("origin")), grades=variability.GRADES, cnt=cnt, human=human, nreq=len(req),
|
||||
nmatch=nmatch, ST=verify.ST_NAME,'''),
|
||||
]
|
||||
|
||||
if __name__ == "__main__":
|
||||
patch(APP / "web" / "store.py", STORE)
|
||||
patch(APP / "web" / "app.py", APPPY)
|
||||
print("patched store.py, app.py")
|
||||
Reference in new issue
Block a user