From 12fd03e5656a3bef8d08ab08b821e41caf6ce26d Mon Sep 17 00:00:00 2001 From: kurosawareiji7007-hub Date: Fri, 31 Jul 2026 13:04:01 -0700 Subject: [PATCH] fix(sessions): include approved goals in crystallize summary _approved_artifact_ids_for_session omitted ProposalKind.GOAL after #427, so in-session objectives never appeared on the summary page. Fixes #741 --- CHANGELOG.md | 5 +++++ src/vouch/sessions.py | 15 ++++++++++++--- tests/test_sessions.py | 16 +++++++++++++++- 3 files changed, 32 insertions(+), 4 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 2b0113b4..3cdc4333 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -155,6 +155,11 @@ All notable changes to vouch are documented here. Format follows artifact the caller could not already retrieve, and it touches no write path. ### Fixed +- **session crystallize summary lists approved goals** (#741): + `_approved_artifact_ids_for_session` hard-coded claim/page/entity/relation, + so a goal approved in-session never appeared under "Crystallized + artifacts" after goals landed (#427). GOAL is included with the other + create kinds; DELETE stays excluded (no new artifact id). - **`extract` no longer fractures file paths/URLs into auto-approved garbage claims** (#702): the sentence segmenter only skipped a `.` as a boundary when it was flanked by digits on both sides (decimals/versions diff --git a/src/vouch/sessions.py b/src/vouch/sessions.py index a0079366..e99905a8 100644 --- a/src/vouch/sessions.py +++ b/src/vouch/sessions.py @@ -169,11 +169,20 @@ def _build_summary_body(sess: Session, ids: list[str]) -> str: def _approved_artifact_ids_for_session(store: KBStore, session_id: str) -> list[str]: + # Every create-kind proposal belongs on the summary. DELETE is inverse + # (no new artifact id). GOAL must be included — omitting it after #427 + # left approved objectives invisible on the crystallize page. + _SUMMARY_KINDS = frozenset({ + ProposalKind.CLAIM, + ProposalKind.PAGE, + ProposalKind.ENTITY, + ProposalKind.RELATION, + ProposalKind.GOAL, + }) ids = { str(pr.payload.get("id")) for pr in store.list_proposals(ProposalStatus.APPROVED) - if pr.session_id == session_id and pr.kind in { - ProposalKind.CLAIM, ProposalKind.PAGE, ProposalKind.ENTITY, ProposalKind.RELATION, - } and pr.payload.get("id") + if pr.session_id == session_id and pr.kind in _SUMMARY_KINDS + and pr.payload.get("id") } return sorted(ids) diff --git a/tests/test_sessions.py b/tests/test_sessions.py index 917b5d05..01289b96 100644 --- a/tests/test_sessions.py +++ b/tests/test_sessions.py @@ -8,7 +8,7 @@ import pytest from vouch import sessions as sess_mod -from vouch.proposals import approve, propose_claim +from vouch.proposals import approve, propose_claim, propose_goal from vouch.storage import KBStore @@ -246,3 +246,17 @@ def now(tz: object = None) -> _dt.datetime: body2 = store.get_page(second["summary_page_id"]).body assert body1 == body2 + + +def test_crystallize_summary_includes_approved_goals(store: KBStore) -> None: + """Approved GOAL proposals are create-kind artifacts — must list on summary.""" + sess = sess_mod.session_start(store, agent="agent", task="ship") + pr = propose_goal( + store, title="land the release", proposed_by="agent", session_id=sess.id, + ) + goal = approve(store, pr.id, approved_by="human") + sess_mod.session_end(store, sess.id) + + result = sess_mod.crystallize(store, sess.id, approver="human") + body = store.get_page(result["summary_page_id"]).body + assert f"`{goal.id}`" in body