fix(498-1 렌더러 멈춤): 밀린 버림 차례가 같은 기록을 되받는 finishDrop 고리 그침 — 요구 298-3

- design_pipeline_page finishDrop — lastRun 이 이미 끝난 같은 기록을 돌려주면 다시 부르지 않고 그 기록으로 마침(마이크로태스크 고리 · 렌더러 메모리 4.7 GB 뒤 죽음)
- 시험 test_298_3_finishdrop_loop(finishDrop 본문을 떼어 같은 기록 되받기 → 한 번만 · 고치기 전 51 번)

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
This commit is contained in:
2026-10-20 22:49:19 +09:00
co-authored by Claude Opus 5.5
parent 4d757a43f2
commit fcb3c994b3
3 changed files with 92 additions and 2 deletions
+4 -2
View File
@@ -375,8 +375,10 @@ function createPagePipeline(
const finishDrop = async (record: PipelineRunRecord): Promise<string> => {
// 뒤 차례(B06 cross 등)에 밀림 · 초안은 그대로 버려짐 — 마지막 차례 끝에서 되돌림(B05 와 같게)
const pushed = record.outcome === "dropped" && record.generation !== pipe.generation;
if (pushed && dropPending && !pipe.input("route"))
return (lastRun as Promise<PipelineRunRecord>).then(finishDrop);
if (pushed && dropPending && !pipe.input("route")) {
const next = await (lastRun as Promise<PipelineRunRecord>);
if (next !== record) return finishDrop(next); // 같은 기록 = 끝난 차례 · 되받으면 끝없는 고리(298-3)
}
dropPending = false;
const routeId = pageSaved.canonRouteId();
const stations = pipe.output("ground")?.stations ?? [];
@@ -0,0 +1,63 @@
/* 298-3 노선 [저장] 뒤 렌더러 멈춤 단위검증 헬퍼 — `design_pipeline_page.ts` 의 `finishDrop` 본문을
* 그대로 떼어 트랜스파일 · 둘레 칸은 가짜. 밀린 버림 기록(dropped · 옛 차례)을 넘기고 lastRun 이 같은 기록을
* 되돌려 줄 때 finishDrop 이 몇 번 불리는지 · 끝나는지. 결과 JSON 을 stdout 으로.
*/
const fs = require("fs");
const path = require("path");
const ts = require(path.join(__dirname, "..", "..", "config", "node_modules", "typescript"));
const ROOT = path.join(__dirname, "..", "..");
const code = fs.readFileSync(
path.join(ROOT, "A00_Common", "design", "design_pipeline_page.ts"),
"utf8",
);
const begin = code.indexOf("const finishDrop = async");
const end = code.indexOf("\n };", begin) + 5;
if (begin < 0 || end < begin) throw new Error("finishDrop 못 찾음");
const body = code
.slice(begin, end)
.replace("const finishDrop = async", "const finishDropInner = async");
const harness = `
let calls = 0;
const finishDrop = (record: any): Promise<string> => {
calls += 1;
if (calls > 50) return Promise.reject(new Error("loop"));
return finishDropInner(record);
};
${body}
return { finishDrop, calls: () => calls };
`;
const js = ts
.transpileModule(`(function () {${harness}})`, {
compilerOptions: { target: ts.ScriptTarget.ES2022 },
})
.outputText.replace(/^"use strict";/, "");
const record = { outcome: "dropped", generation: 3 };
const pipe = { generation: 5, input: () => null, output: () => null };
let released = 0;
const scope = {
pipe,
dropPending: true,
lastRun: Promise.resolve(record), // 이미 끝난 같은 차례 기록
pageSaved: { canonRouteId: () => 7, adoptCanon() {} },
held: null,
options: {},
heldSectionDetail: () => null,
stationDiff: () => null,
adoptAfterDrop() {},
publisher: { shown: async () => undefined },
releaseWhenQuiet: async () => void (released += 1),
};
const names = Object.keys(scope);
const make = new Function(
"scope",
`let { ${names.join(", ")} } = scope;\nreturn ${js.trim().replace(/;$/, "")}();`,
);
const { finishDrop, calls } = make(scope);
finishDrop(record).then(
(why) => console.log(JSON.stringify({ calls: calls(), why, released, record })),
(error) => console.log(JSON.stringify({ calls: calls(), error: String(error), released })),
);
@@ -0,0 +1,25 @@
"""298-3 노선 [저장] 뒤 렌더러 멈춤 — 밀린 버림 기록을 lastRun 이 같은 기록으로 되돌려 줘도 finishDrop 은 한 번.
./venv/Scripts/python.exe -m pytest resources/tester/test_298_3_finishdrop_loop.py -q
"""
from __future__ import annotations
import json
import subprocess
from pathlib import Path
HELPER = Path(__file__).resolve().parent / "helper_298_3_finishdrop_loop.cjs"
def test_같은_기록_되받기_고리_없음() -> None:
run = subprocess.run( # noqa: S603 — 고정 실행 파일
["node", str(HELPER)], capture_output=True, text=True, encoding="utf-8", timeout=30
)
assert run.returncode == 0, run.stderr
out = json.loads(run.stdout)
assert "error" not in out, out
assert out["calls"] == 1
assert out["why"] == "dropped" # 밀린 기록 그대로 · 되돌림 없음
assert out["released"] == 1
assert out["record"]["restored"] is False