feat(M01): 흐름 로직 입력 약속 하나 — 저장 때 「입력」 은 흐름의 설계 때 정할 것으로 다시 씀 · 지우는 것은 계산 칸만 · 시험판 길 지움

- 흐름으로 저장 = 계산 칸(중간 · 호표 · 결과 · 덧줄)만 지우고 「입력」(이름 · 설명 · 단위 · 고르기 · 범위)은 흐름에서 같은 꼴로 · 조합 · 구조물 · 미리 보기 · 다시 적용은 「입력」 을 그대로 읽음(따로 갈래 없음)
- 엔진 갈래: 안 준 입력 = 멈춤 · 빈 값으로 준 입력 · 단가 없는 품목 · 표에 없는 조건 = 그 값만 비움(알림) · 결과 꼴 = 돈 출력 줄 · 값들 · 알림
- 프로젝트에서 고른 자재 = 흐름 재료 자리 이름으로(고름 · 다시 적용 때 살림)
- 시험판 길 지움: /list · /get · /save · /delete · /user-elements(서버) · cfList · cfGet · cfSave · cfDelete · cfUserElement*(Api) · 빈 _data 폴더
- 시험: 조합에 담은 로직을 흐름으로 바꿔도 입력 목록 · 로직별 결과 · 비목 합 · 단가 묶음 셈이 같음

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01KmELpdXFDRZHhfbGuRhSmL
This commit is contained in:
2026-10-02 09:57:30 +09:00
co-authored by Claude Opus 5.5
parent 7418571b63
commit 3336902c17
13 changed files with 274 additions and 455 deletions
@@ -36,7 +36,7 @@ _ARITH = re.compile(r"[\d.\s+\-*/()]*\d[\d.\s+\-*/()]*") # 분수·식 값 칸(
FLOW_RUN = None
# 흐름 로직 입력 = 「설계 때 정할 것」 → 입력 꼴(이름 · 단위 · 고르기 · 범위 · 예시) — 서버가 꽂음
FLOW_INPUTS = None
OLD_FIELDS = ("입력", "중간", "호표", "결과", "덧줄") # 흐름 로직 줄에는 없음(흐름 하나가 정본)
CALC_FIELDS = ("중간", "호표", "결과", "덧줄") # 계산 칸 — 흐름 로직 줄엔 없음(입력은 둠)
def _flow_hooks() -> None:
@@ -49,11 +49,9 @@ def _flow_hooks() -> None:
def inputs_of(row: dict) -> list[dict]:
"""로직 입력 — 흐름 로직이면 흐름의 「설계 때 정할 것」 · 아니면 줄의 「입력」."""
if isinstance(row.get("흐름"), dict):
_flow_hooks()
return FLOW_INPUTS(row)
return row.get("입력", [])
"""흐름의 「설계 때 정할 것」 → 입력 꼴(이름 · 단위 · 고르기 · 범위 · 예시)."""
_flow_hooks()
return FLOW_INPUTS(row)
class FormulaError(ValueError):
@@ -508,7 +506,7 @@ def fill_empty(row: dict, given: dict) -> dict:
"""화면이 안 보낸 가격 입력(단위에 「원」)은 null 로 채움 — 빈 입력과 같이 그 줄만 비움."""
gap = {
s["이름"]: None
for s in inputs_of(row)
for s in row.get("입력", [])
if s["이름"] not in given and "원" in str(s.get("단위") or "")
}
return {**given, **gap}
@@ -665,7 +663,7 @@ def run(master: Master, ref: str, given: dict, depth: int = 0) -> dict:
# ── 검사 ──────────────────────────────────────────────────────────────
def _choices(row: dict, name: str, master: Master):
"""이름이 가질 수 있는 값 — 고르기 입력, 또는 고르기 입력만 끼운 요소 칸인 중간. 모르면 None."""
for spec in inputs_of(row):
for spec in row.get("입력", []):
if spec["이름"] == name:
return spec.get("고르기")
for step in row.get("중간", []):
@@ -753,7 +751,7 @@ def _check_node(row, node, known: set, master: Master, where: str, out: list, ca
except FormulaError as e:
out.append(f"{where} · {e}")
continue
names = {s["이름"] for s in inputs_of(target)}
names = {s["이름"] for s in target.get("입력", [])}
given = {k for k, _ in part[2]}
for extra in sorted(given - names):
out.append(f"{where} · 「{master.label(part[1])}」 에 입력 「{extra}」 없음")