feat(740-1): 일위대가 안 일위대가 = 사본 블록(원본 연결 끊음 · 하위 호표 끝수 그대로) · 초기화 이음 기계 다시 받음

- SC 안 SC = 안쪽 SC 몸 통째를 그 줄 참조 꼴 {층: 사본, 주인: 몸 지문, 키: 본 SC, 몸} 에 담음(Labor_Ref.copy_ref · read_ref 가 몸을 돌려줌 → 셈 · 알약 · B09 하위 대가 블록 · 지문 = 옛 참조 줄과 같은 길) · 엑셀 대조 거창 같음 14 그대로 · 같은 기초 겹침도 블록 안(400 없음)
- 저장 · 읽기 · 옮기기 · 찾아 넣기 모두 사본 블록 · 자기 자신을 담은 줄 = 안 펼침(저장 422 그대로)
- 단가산출에 SC 넣기 = 사본 블록 줄도 더한줄로 펼침(바깥 × 안 수량)
- 초기화 = 사본 몸 안까지 · 이음 기계 고정 줄(736-3) 단가 = M01 기계 한 시간 값 다시 받음
- 옮기기 = 쓰는 곳 검사 걷음(같은 자리 한 줄이라 안 줄 알약 차례 그대로) · 실 storage 보기만 = 1/3 SC-000002 사본 블록 · 전후 1,446,078 같음
- 낡은 시험 고침(까닭 = 하위 호표 참조가 사본 블록으로): m02_labor_ref(순환 대신 사본 · 복사 딸림 없음) · 733-2(딸림 재사용 = 구조물 조합 복사) · 735-1(쓰는 곳 = 구조물 조합 둘) · hopyo_build · 707-7(하위 호표 줄 = 사본 블록 · 본뜬)

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
This commit is contained in:
2026-11-02 20:17:17 +09:00
co-authored by Claude Opus 5.5
parent 6c0211af1f
commit 6893599fa9
8 changed files with 206 additions and 164 deletions
+41 -1
View File
@@ -42,6 +42,7 @@ _OWNER = {"company": re.compile(r"^\d+$"), "personal": re.compile(r"^\d+/\d+$")}
Ref = dict[str, Any]
Here = tuple[str, str | None]
COPY = "사본" # 740-1 하위 호표 사본 블록 — 참조 꼴 {층: 사본, 주인: 몸 지문, 키: 본 SC, 몸}
def _bad(text: str, status: int = 400) -> store.StoreError:
@@ -103,7 +104,28 @@ def ref_path(ref: Ref) -> Path:
return folder_of(ref["층"], ref.get("주인")) / kind_of(ref["키"]) / f"{ref['키']}.json"
def is_copy(ref: Any) -> bool:
"""사본 블록(740-1) — 일위대가 안 일위대가 몸을 줄 안에 담음(원본 연결 없음 · 키 = 출처)."""
return isinstance(ref, dict) and ref.get("층") == COPY and isinstance(ref.get("몸"), dict)
def copy_ref(body: dict[str, Any], key: str) -> Ref:
"""사본 블록 참조 — 몸(종류 · 이름 · 단위 · 담은로직 · 자재 · 본뜬) · 주인 = 몸 지문."""
import hashlib
keep = {k: body[k] for k in ("종류", "이름", "단위", "담은로직", "자재", "본뜬") if body.get(k)}
raw = json.dumps(keep, ensure_ascii=False, sort_keys=True, default=str)
return {
"층": COPY,
"주인": hashlib.sha256(raw.encode()).hexdigest()[:12],
"키": key,
"몸": keep,
}
def read_ref(ref: Ref) -> dict[str, Any] | None:
if is_copy(ref):
return ref["몸"]
try:
got = json.loads(ref_path(ref).read_bytes())
except (OSError, ValueError):
@@ -114,6 +136,8 @@ def read_ref(ref: Ref) -> dict[str, Any] | None:
def readable(at: Here, ref: Ref) -> bool:
"""`at` 층이 `ref` 를 가리켜도 되나 — 위 층만(프로젝트는 아무것도 · 굳힌 사본만 씀)."""
layer, owner = at
if ref.get("층") == COPY: # 사본 블록 = 줄 안 몸(어느 층도 읽음)
return True
if layer == "project":
return False
if ref["층"] == "system":
@@ -127,6 +151,8 @@ def check_ref(raw: Any, at: str, here_: Here) -> Ref:
"""참조 꼴 · 위 층만 · 있는 문서 — 아니면 400."""
if not isinstance(raw, dict):
raise _bad(f"{at} · 참조는 {{층, 주인, 키}}")
if raw.get("층") == COPY:
return _check_copy(raw, at)
layer, owner, key = raw.get("층"), raw.get("주인"), str(raw.get("키") or "")
if layer == "system":
owner = None
@@ -142,6 +168,20 @@ def check_ref(raw: Any, at: str, here_: Here) -> Ref:
return ref
def _check_copy(raw: Ref, at: str) -> Ref:
"""사본 블록 검사 — 키 = SC · 몸 줄 · 자재 = 일위대가 검사 그대로 · 지문 다시 셈."""
from M02_MasterTemplete import M02_Labor_Doc as labor
body, key = raw.get("몸"), str(raw.get("키") or "")
if not isinstance(body, dict) or not _KEY.match(key) or kind_of(key) != "precombo":
raise _bad(f"{at} · 사본 블록은 {{층: 사본, 키: SC-…, 몸}}")
try:
labor.clean({"담은로직": body.get("담은로직") or [], "자재": body.get("자재") or []})
except store.StoreError as e:
raise _bad(f"{at} · 사본 블록 · {e.detail}") from e
return copy_ref(body, key)
# ── 서고 문서 정리 ─────────────────────────────────────────────────────
def check_key(kind: str, name: str) -> None:
found = _KEY.match(name)
@@ -158,7 +198,7 @@ def clean_doc(kind: str, doc: Any) -> dict[str, Any]:
if here()[0] != "project": # 740-1 — M02 참조 줄 = M01 키 줄로 펼침(못 펼친 줄은 그대로)
from M02_MasterTemplete import M02_Library_Flat as flat
doc = flat.flat_doc(kind, doc)
doc = flat.flat_doc(kind, doc, me={"층": here()[0], "주인": here()[1], "키": key})
name, unit = str(doc.get("이름") or "").strip(), str(doc.get("단위") or "").strip()
if kind == "sangeun":
from M02_MasterTemplete import M02_Sangeun_Calc as calc