feat(365-2): 횡단 고침 → 묶음 cross 입력 · 입력 지문 고리(최대 3바퀴)

- 횡단 입력 = 부를 때마다 저장 상세 + 세션 고침(지반 유형 · 측구 · 2단 경사 · 암 경계 · 암 경사 · 측점 열쇠) + 표준 — 상세 객체마다 기억 걷음
- B06 고침(DesignSync 다시 셈 끝 · 암 경계) 뒤 pipelineCross — 노선 초안 동안 B06 고침이 카드(새 노선 상세)에 보임
- 고리 — 열 때 · 페이지 들어올 때 입력(세션 · 가게)을 묶음 입력(넘긴 값 · follow 뒤 값)과 견줌 · 다르면 한 바퀴 · 끝난 뒤 다시 · 3바퀴 넘으면 「계산 불안정」 한 줄
- 시험 test_365_page_pipeline(초안 동안 고침 카드 · 같은 입력 0바퀴 · 세션만 바뀜 1바퀴 · 불안정 3바퀴 알림 1)

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01TteK8mfYZetk5P3bEZkyVZ
This commit is contained in:
2026-10-17 12:08:22 +09:00
co-authored by Claude Opus 5.5
parent 4c017c3bd9
commit 7efee22ab4
8 changed files with 203 additions and 22 deletions
+53 -1
View File
@@ -199,6 +199,7 @@ async function run() {
const savedBase = await savedBaseOf();
const shop = store.useDesignStore(P, null);
const holds = [];
let unstable = 0;
const link = () => ({
pipelineSource: () => ({
base: savedBase,
@@ -215,6 +216,7 @@ async function run() {
holds.push(saved === job.detail ? "saved" : saved ? "other" : "null");
return async () => "saved";
},
onUnstable: () => (unstable += 1),
};
const open = (label) => {
const bridge = pageMod.routeDraftBridge(P, link(), options);
@@ -249,6 +251,56 @@ async function run() {
stages: Object.keys(reopenRun.stages),
};
// ⑤ 초안 동안 B06 고침 — 세션 칸(측점 열쇠) → pipelineCross → 카드(묶음 새 노선 상세)
const pipe = pageMod.pagePipeline(P);
const target = pipe.output("ground").stations.find((s) => s.kind === "grid" && s.chainageM > 30);
const cell = (ground) => ({ [target.key]: { ground_type: ground, section_mode: "left_cut" } });
const groundAt = () =>
pageMod
.pipelineDetail(P)
.cross_sections.find((s) => Math.abs(s.chainage_m - target.chainageM) < 1e-3)?.design
?.ground_type ?? null;
const before = groundAt();
page.writeState("crossdesign", cell("blasting_rock"), P, 7);
let mark = runs().length;
pageMod.pipelineCross(P);
await settle(mark + 1);
await tick(20);
const edit = {
key: target.key,
before,
after: groundAt(),
input: pipe.input("cross").byKey[target.key]?.groundType ?? null,
runs: runs().length - mark,
sources: runs()[runs().length - 1].sources,
};
// ⑥ 고리 — 같은 입력 = 0 바퀴 · 세션만 바뀜(알림 없음) = 1 바퀴 · 안 맞음 = 3 바퀴 뒤 알림
mark = runs().length;
const same = await pageMod.reconcilePagePipeline(P);
const sameRuns = runs().length - mark;
page.writeState("crossdesign", cell("ripping_rock"), P, 7);
mark = runs().length;
const once = await pageMod.reconcilePagePipeline(P);
const loop = {
same,
sameRuns,
once,
onceRuns: runs().length - mark,
onceGround: groundAt(),
};
let n = 0;
const realStandard = STUBS["B06_Section_UI_Standard_Panel"].effectiveStandardCross;
STUBS["B06_Section_UI_Standard_Panel"].effectiveStandardCross = () => ({
...job.standard,
n: (n += 1),
});
mark = runs().length;
loop.unstable = await pageMod.reconcilePagePipeline(P);
loop.unstableRuns = runs().length - mark;
loop.alerts = unstable;
STUBS["B06_Section_UI_Standard_Panel"].effectiveStandardCross = realStandard;
await pageMod.reconcilePagePipeline(P);
// ④ 초안 버림 — 열 때 맡긴 초안 전 상세로 되돌림(저장 정본)
again.bridge.edited(null);
const dropRun = await settle(runs().length + 1);
@@ -260,7 +312,7 @@ async function run() {
};
pageMod.disposePagePipeline(P);
page.clearDrafts(P);
return { kept, b05, b06, dropped };
return { kept, b05, b06, dropped, edit, loop };
}
run()
@@ -5,6 +5,9 @@
- 노선 초안 둔 채 다시 열기(새로고침 · B06 바로 열기 · 편집기 없음) = B05 길과 같은 초안 상세
(측점 · 계획고 · 면적) · 측점을 가게에 다시 냄 · 초안 전 = 저장 상세(열 때 맡김) → 버리면 saved.
- 초안 동안 측점 갈아끼움은 메모리만 — 세션 상세(저장 노선)를 새 측점 + 옛 설계로 덮지 않음.
못박는 것(365-2)
- 초안 동안 B06 고침(세션 칸 · 측점 열쇠) → `pipelineCross` → 묶음 cross 입력 · 카드에 보임.
- 고리 — 같은 입력 = 0 바퀴 · 세션만 바뀜 = 1 바퀴 · 안 맞으면 3 바퀴 뒤 「계산 불안정」 한 번.
짝: `helper_365_page_pipeline.cjs`(진짜 TS · 서버 창구만 가짜 · 틀 = 346-2 시험) ·
`helper_365_swap_entry.ts` + `helper_365_swap.mjs`(진짜 상세 가게 · vite ssr).
@@ -99,6 +102,20 @@ def test_365_1_초안_버림_저장_정본(r):
assert d["restoreWhy"] == "saved"
def test_365_2_초안_동안_횡단_고침_카드에(r):
e = r["edit"]
assert e["before"] != "blasting_rock"
assert e["input"] == "blasting_rock" and e["after"] == "blasting_rock"
assert e["runs"] == 1 and e["sources"] == ["cross"]
def test_365_2_고리_바퀴_수(r):
loop = r["loop"]
assert loop["same"] == 0 and loop["sameRuns"] == 0 # 같은 입력 = 안 돎
assert loop["once"] == 1 and loop["onceRuns"] == 1 and loop["onceGround"] == "ripping_rock"
assert loop["unstable"] == -1 and loop["unstableRuns"] == 3 and loop["alerts"] == 1
@pytest.fixture(scope="module")
def swap(tmp_path_factory) -> dict:
work = tmp_path_factory.mktemp("b365")