fix(projection): the title is derived too
All checks were successful
CI Smoke / host-smoke (push) Successful in 0s
CI Smoke / container-smoke (push) Successful in 1s
Build and Publish Multi-Context Image / build-and-push (push) Successful in 24s

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 <noreply@anthropic.com>

Assistant: claude-code
Assistant-Model: opus
Assistant-Process: 2583210@bnt-lap001
Assistant-Session: f2bff2d5-e9b2-4338-92ca-10282a927006
This commit is contained in:
tegwick 2026-08-28 15:32:29 +02:00
parent fadfddb6c6
commit 500049fc31
2 changed files with 63 additions and 1 deletions

View file

@ -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"