fix(run-ledger): 파일은 안 깨지는데 두 세션의 기록이 섞였다
이어받은 단계에 앞 세션이 관문을 써도 그대로 들어갔다. 원자성 문제가 아니다 — 파일은 온전한 채로 두 세션의 기록이 섞인다. 그리고 관문마다 누가 적었는지가 없어서 나중에 원장을 읽어도 가릴 수 없었다. begin 이 세대를 올리고 주인을 적는다. 낮은 세대나 다른 주인의 쓰기는 거절한다. bin/task.py 의 attempt 와 같은 자리다. gate 와 end 도 --session 을 받아 적는다. 관문마다 session·generation·at 이 남고 end 는 finishedBy 를 남긴다. 주인이 없는 단계는 그대로 받는다. 세션을 안 쓰는 단일 세션 사용이 깨지지 않는다. 회귀에 그 대조를 넣었다. Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01Q4vKjQo9KKBBokzxqXLCfk
This commit is contained in:
co-authored by
Claude Opus 5
parent
8ca822dc4d
commit
96d7fbc57a
@@ -140,6 +140,50 @@ class LedgerTest(unittest.TestCase):
|
||||
_cli("rider", self.led, "--id", "R12", "--why", "x", "--seconds", "60")
|
||||
self.assertEqual("S3", self._read()["riders"][0]["duringStage"])
|
||||
|
||||
# ── 두 세션의 기록이 섞이지 않는다 ────────────────────────────────
|
||||
def test_a_superseded_session_cannot_write(self):
|
||||
"""파일이 안 깨지는 것과 두 세션의 기록이 안 섞이는 것은 다른 일이다.
|
||||
|
||||
`plan/02` B-005 — 「이전 작업자의 종료를 확인하고 재배정한 시도 번호와 맞는
|
||||
결과만 받는다.」 `bin/task.py` 의 attempt 와 같은 자리다.
|
||||
"""
|
||||
_cli("begin", self.led, "--stage", "S3", "--session", "sess-1")
|
||||
_cli("begin", self.led, "--stage", "S3", "--session", "sess-2")
|
||||
for args in (("gate", self.led, "--stage", "S3", "--cmd", "stale", "--exit", "0"),
|
||||
("end", self.led, "--stage", "S3", "--status", "DONE", "--echo", "x")):
|
||||
with self.subTest(cmd=args[0]):
|
||||
p = _cli(*args, "--session", "sess-1")
|
||||
self.assertEqual(2, p.returncode)
|
||||
self.assertIn("sess-2", p.stderr)
|
||||
|
||||
def test_a_stale_generation_is_refused(self):
|
||||
_cli("begin", self.led, "--stage", "S3", "--session", "sess-1")
|
||||
_cli("begin", self.led, "--stage", "S3", "--session", "sess-2")
|
||||
p = _cli("gate", self.led, "--stage", "S3", "--cmd", "x", "--exit", "0",
|
||||
"--session", "sess-2", "--generation", "1")
|
||||
self.assertEqual(2, p.returncode)
|
||||
|
||||
def test_an_anonymous_write_to_an_owned_stage_is_refused(self):
|
||||
"""누가 썼는지 안 밝히면 나중에 원장을 읽을 수 없다."""
|
||||
_cli("begin", self.led, "--stage", "S3", "--session", "sess-1")
|
||||
p = _cli("gate", self.led, "--stage", "S3", "--cmd", "x", "--exit", "0")
|
||||
self.assertEqual(2, p.returncode)
|
||||
|
||||
def test_the_current_owner_writes_and_is_recorded(self):
|
||||
"""대조군. 지금 주인의 쓰기는 들어가고, 누가 어느 세대에 썼는지 남는다."""
|
||||
_cli("begin", self.led, "--stage", "S3", "--session", "sess-1")
|
||||
p = _cli("gate", self.led, "--stage", "S3", "--cmd", "ok", "--exit", "0",
|
||||
"--session", "sess-1", "--generation", "1")
|
||||
self.assertEqual(0, p.returncode, p.stderr)
|
||||
gate = self._stage("S3")["gates"][0]
|
||||
self.assertEqual(("sess-1", 1), (gate["session"], gate["generation"]))
|
||||
|
||||
def test_an_unowned_stage_still_accepts_writes(self):
|
||||
"""주인이 없으면 (세션을 안 쓰는 단일 세션 사용) 그대로 쓴다. 기존 쓰임을 안 깬다."""
|
||||
_cli("begin", self.led, "--stage", "S3")
|
||||
self.assertEqual(0, _cli("gate", self.led, "--stage", "S3",
|
||||
"--cmd", "x", "--exit", "0").returncode)
|
||||
|
||||
# ── 옛 원장을 깨지 않는다 ─────────────────────────────────────────
|
||||
def test_the_added_fields_do_not_break_the_verifier(self):
|
||||
"""이 도구가 더한 칸이 있어도 검사기가 그대로 읽어야 한다."""
|
||||
|
||||
Reference in New Issue
Block a user