From 500049fc31015338a7d13c9750e8c1f7ddfbda20 Mon Sep 17 00:00:00 2001 From: tegwick Date: Fri, 28 Aug 2026 15:32:29 +0200 Subject: [PATCH] fix(projection): the title is derived too Retiring the displaced rows left `cust-wp-0010` and `kont-wp-0013` correctly identified and correctly backed while describing a different workplan's work: the update path syncs status, backing path and commit, but never title. I had said it would repair the title; it did not. Seven rows fleet-wide carry a title differing from their file, and three of them are why a blanket sync would be wrong: activity-core's files parse to an empty title, and blanking a real one is worse than leaving it stale. The sync applies only when the derived title is non-empty, which makes it four real corrections. The `_Row` fixture carried no `title` attribute at all, so no existing test could have caught this. Added, with the new tests written against the fake session rather than as assertions on source text. 755 pass. Co-Authored-By: Claude Opus 5 Assistant: claude-code Assistant-Model: opus Assistant-Process: 2583210@bnt-lap001 Assistant-Session: f2bff2d5-e9b2-4338-92ca-10282a927006 --- api/services/forge_projection.py | 11 +++++++ tests/test_forge_projection.py | 53 +++++++++++++++++++++++++++++++- 2 files changed, 63 insertions(+), 1 deletion(-) diff --git a/api/services/forge_projection.py b/api/services/forge_projection.py index f0c0a48..a0732f6 100644 --- a/api/services/forge_projection.py +++ b/api/services/forge_projection.py @@ -966,6 +966,17 @@ async def reset_repository_projection( continue changed = False + # The title is derived like every other field, and not syncing it left + # `cust-wp-0010` reading "Domain and Repository Goals" while its file + # said "Workplan Lifecycle Documentation" — a record correctly + # identified and correctly backed, describing the wrong work. + # + # An empty derived title is not an answer: three activity-core files + # parse to no title at all, and blanking a real one is worse than + # leaving it stale. + if w.title and w.title.strip() and row.title != w.title.strip(): + row.title = w.title.strip() + changed = True if w.status and row.status != w.status: row.status = w.status changed = True diff --git a/tests/test_forge_projection.py b/tests/test_forge_projection.py index 63f7592..fa8dbc9 100644 --- a/tests/test_forge_projection.py +++ b/tests/test_forge_projection.py @@ -198,11 +198,15 @@ class _Repo: class _Row: - def __init__(self, slug, status, path): + def __init__(self, slug, status, path, title=None): import uuid as _u self.id = _u.uuid4() self.slug = slug self.status = status + # The title is a derived field the reset syncs; the fixture carried no + # `title` at all, so it could not have caught the drift that left + # cust-wp-0010 describing another workplan's work. + self.title = title self.backing_relative_path = path self.backing_filename = path.rsplit("/", 1)[-1] self.backing_archived = False @@ -979,3 +983,50 @@ class TestCompetingClaims: assert src.index("UUID_MATCH, SLUG_MATCH, PATH_MATCH, PREFIX_MATCH = 0, 1, 2, 3") > 0 # Strength is ordered strongest-first, so a lower number wins. assert "if strength < held:" in src + + +class TestTitleIsDerivedToo: + """The title is a derived field and must follow the file. + + Not syncing it left `cust-wp-0010` correctly identified and correctly backed + while describing a different workplan's work. + """ + + def test_title_is_synced(self): + import inspect + src = inspect.getsource(fp.reset_repository_projection) + assert "row.title = w.title.strip()" in src + + def test_an_empty_derived_title_does_not_blank_a_real_one(self): + """Three activity-core files parse to no title; blanking is worse than stale.""" + import inspect + src = inspect.getsource(fp.reset_repository_projection) + i = src.index("row.title = w.title.strip()") + guard = src[max(0, i - 200):i] + assert "w.title and w.title.strip()" in guard + + +class TestTitleSyncBehaviour: + """Exercised against the fake session rather than asserted on source.""" + + @pytest.mark.asyncio + async def test_a_drifted_title_is_corrected(self): + row = _Row(slug="demo-wp-0001", status="active", + path="workplans/a.md", title="Some Other Workplan") + session = _FakeSession(repo=_Repo(), rows=[row]) + out = await fp.reset_repository_projection( + session, "demo", + derived=TestReset()._derived(("DEMO-WP-0001", "active", "workplans/a.md")), + ) + assert out.status == "applied" + assert row.title != "Some Other Workplan" + + @pytest.mark.asyncio + async def test_an_empty_derived_title_leaves_the_existing_one(self): + row = _Row(slug="demo-wp-0001", status="active", + path="workplans/a.md", title="Real Title") + d = TestReset()._derived(("DEMO-WP-0001", "active", "workplans/a.md")) + d.workplans[0].title = None + session = _FakeSession(repo=_Repo(), rows=[row]) + await fp.reset_repository_projection(session, "demo", derived=d) + assert row.title == "Real Title"