feat(826-2): M01 기계 = 한 대 한 파일 · M01 저장 길 줄이기
- 기계.json 한 파일 → 폴더 기계/(EQ 키마다 파일 + _머리.json · 차례 = 키 차례) · 옮긴 빌더 build_기계폴더_826_2 (655 대 머리 · 줄 · 차례 대조 뒤 옛 파일 지움) - 읽기 도우미 master_machine_files — cm.load · Store_Cache · Store.read 가 옛 「기계.json」 한 표 꼴로 지어 줌(API · 화면 · 색인 그대로) · 쓰기 = 바뀐 대 파일만 - 기계.json 직접 열던 곳 도우미로: M02_San_Basis_Index · M02_Logic_Standard 지문 · build_용도 · master_keys.adopt · _틀 · _갱신절차 - 저장 몸 M01_MasterData_Store_Save — 전 = 기억 · 후 = 고친 파일만 얕은 사본 · 틀은 고친 줄만 · 조합 · 일위대가 문서는 로직 줄이 빠질 때만 · 재료 찾기 기억 이어받음 · 저장 뒤 쓴 파일만 기억에 갈아 끼움 - 마스터 사본 시험 22 개 = 기계 폴더도 복사 · 821_2 · 815_2 글 대조 = 대 파일 하나 대조(옛 한 파일 글 대조 낡음) - 새 시험 test_826_2 — 폴더 = 한 표 · 한 대 한 파일 · 새 · 지움 · 409 · 옛 길과 같은 오류 스무 견본 Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
This commit is contained in:
@@ -57,10 +57,12 @@ def _now(folder: Path) -> dict[str, tuple]:
|
||||
def _stamps(folder: Path) -> dict[str, tuple]:
|
||||
"""첫 층 JSON 자국 — {이름: (시각, 크기)} · 밑줄 파일은 `cm.load` 가 안 읽으므로 뺌."""
|
||||
out = {}
|
||||
for path in sorted(folder.glob("*.json")):
|
||||
if not path.name.startswith("_"):
|
||||
stat = path.stat()
|
||||
out[path.name] = (stat.st_mtime_ns, stat.st_size)
|
||||
for name in cm.mmf.names(folder):
|
||||
if name == cm.mmf.NAME and cm.mmf.here(folder): # 기계 폴더(826-2) — 대마다 자국
|
||||
out[name] = cm.mmf.stamp(folder)
|
||||
elif not name.startswith("_"):
|
||||
stat = (folder / name).stat()
|
||||
out[name] = (stat.st_mtime_ns, stat.st_size)
|
||||
return out
|
||||
|
||||
|
||||
@@ -127,6 +129,29 @@ def stale(names, marks=()) -> None:
|
||||
held.clear()
|
||||
|
||||
|
||||
def adopt(folder: Path, files: dict[str, dict], whole, names, marks=()) -> None:
|
||||
"""저장 뒤 — 쓴 파일(`names`)만 기억에 갈아 끼움(다시 읽지 않음 · `files` · `whole` = 저장한 뒤
|
||||
모습). 흔들린 자리는 `stale` 처럼 적어 둠. 그 사이 다른 파일도 바뀌었으면 `stale` 로."""
|
||||
with _FRESH:
|
||||
now = _stamps(folder)
|
||||
rest = [n for n in now if n not in names]
|
||||
if (
|
||||
_INDEX.get("폴더") != folder
|
||||
or set(now) != set(_READ) | set(names)
|
||||
or any(_READ[n][0] != now[n] for n in rest)
|
||||
):
|
||||
return stale(names, marks)
|
||||
for name in names:
|
||||
was = _READ.get(name, (None, {}))[1]
|
||||
_READ[name] = (now[name], files[name])
|
||||
_BLOCK.pop(name, None)
|
||||
if was.get("그룹") != "로직":
|
||||
_MARKED.setdefault(name, set()).update(marks)
|
||||
_INDEX.update(자국=now, files={n: _READ[n][1] for n in now}, whole=whole)
|
||||
if (held := _STEADY.get()) is not None:
|
||||
held.clear()
|
||||
|
||||
|
||||
def loaded(folder: Path) -> tuple[dict[str, dict], object]:
|
||||
"""(파일 묶음, 색인) — **읽기 전용**. 고칠 것이면 `cm.load` 로 새로 읽을 것."""
|
||||
with _FRESH:
|
||||
@@ -138,6 +163,9 @@ def loaded(folder: Path) -> tuple[dict[str, dict], object]:
|
||||
del _READ[gone]
|
||||
for name, stamp in now.items():
|
||||
if _READ.get(name, (None,))[0] != stamp:
|
||||
if name == cm.mmf.NAME and cm.mmf.here(folder): # 바뀐 대만 다시 읽음
|
||||
_READ[name] = (stamp, cm.mmf.table(folder, shared=True)[0])
|
||||
continue
|
||||
raw = (folder / name).read_bytes()
|
||||
_READ[name] = (stamp, json.loads(raw, parse_float=Decimal, parse_int=Decimal))
|
||||
if _INDEX.get("자국") != now:
|
||||
@@ -260,20 +288,25 @@ def _labor_files() -> list[tuple[str, Path]]:
|
||||
]
|
||||
|
||||
|
||||
def labor_errors(whole) -> list[str]:
|
||||
def labor_errors(whole, keys=None) -> list[str]:
|
||||
"""일위대가 · 사전조합 문서가 담은 로직 마스터에 없음(39-1d · 50-3) —
|
||||
로직 지우기를 막음 · 어디서 쓰는지 알림 · 참조 줄(로직 없음)은 건너뜀. 전부터 깨진 것은
|
||||
`save` 가 고치기 전 검사와 견주어 뺌."""
|
||||
`save` 가 고치기 전 검사와 견주어 뺌. `keys` = 그 로직 키만 봄(빈 묶음이면 파일을 안 엶)."""
|
||||
out = []
|
||||
if keys is not None and not keys:
|
||||
return out
|
||||
for where, path in _labor_files():
|
||||
try:
|
||||
doc = json.loads(path.read_bytes())
|
||||
raw = path.read_bytes()
|
||||
if keys is not None and not any(k.encode() in raw for k in keys):
|
||||
continue
|
||||
doc = json.loads(raw)
|
||||
except (OSError, ValueError):
|
||||
continue
|
||||
name = (doc.get("일위대가번") if isinstance(doc, dict) else None) or path.stem
|
||||
for one in (doc.get("담은로직") if isinstance(doc, dict) else None) or []:
|
||||
key = str(one.get("로직") or "") if isinstance(one, dict) else ""
|
||||
if not key:
|
||||
if not key or (keys is not None and key not in keys):
|
||||
continue
|
||||
try:
|
||||
whole.logic(key)
|
||||
|
||||
Reference in New Issue
Block a user