Skip to content

Commit 497bb60

Browse files
LeonSGP43teknium1
authored andcommitted
fix(kanban): accept created_cards linked as child of completing task
Widens _verify_created_cards to also accept ids that are children of the completing task in task_links. Previously we only accepted cards where created_by matched the completing task's assignee, which was too strict for legitimate orchestrator flows: a specifier creates a card (so created_by=specifier, not worker), then a worker picks it up and passes parents=[current_task] to kanban_create. The explicit link proves the relationship and should be trusted. Salvaged from NousResearch#20022 @LeonSGP43 (full PR superseded by NousResearch#20232 + this patch; the linked-children relaxation was the portable improvement).
1 parent 0cbb238 commit 497bb60

2 files changed

Lines changed: 64 additions & 11 deletions

File tree

hermes_cli/kanban_db.py

Lines changed: 24 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -1978,14 +1978,23 @@ def _verify_created_cards(
19781978
) -> tuple[list[str], list[str]]:
19791979
"""Partition ``claimed_ids`` into (verified, phantom).
19801980
1981-
A card is "verified" iff a row exists in ``tasks`` with the given id
1982-
AND ``created_by`` matches the completing task's ``assignee`` (or
1983-
the completing task itself — workers that create children of their
1984-
own task also qualify).
1985-
1986-
``phantom`` returns ids that either don't exist at all or exist but
1987-
were not created by the completing worker. The caller decides what
1988-
to do with each bucket; this helper never mutates.
1981+
A card is "verified" iff a row exists in ``tasks`` AND at least one
1982+
of the following holds:
1983+
1984+
* ``created_by`` matches the completing task's ``assignee`` profile
1985+
(the common case: worker A spawns a card via ``kanban_create``,
1986+
which stamps ``created_by=A``).
1987+
* ``created_by`` matches the completing task's id (edge case where
1988+
a worker passed its own task id as the ``created_by`` value).
1989+
* The card is linked as a ``task_links.child`` of the completing
1990+
task — i.e. the worker explicitly called ``kanban_create`` with
1991+
``parents=[<current_task>]``. This accepts cards created through
1992+
the dashboard/CLI by a different principal but then attached to
1993+
the completing task by the worker.
1994+
1995+
``phantom`` returns ids that either don't exist at all, or exist
1996+
but don't satisfy any of the three trust conditions. The caller
1997+
decides what to do with each bucket; this helper never mutates.
19891998
"""
19901999
claimed = [str(x).strip() for x in (claimed_ids or []) if str(x).strip()]
19912000
if not claimed:
@@ -2014,20 +2023,24 @@ def _verify_created_cards(
20142023
).fetchall()
20152024
found = {r["id"]: r["created_by"] for r in rows}
20162025

2026+
# Pull the set of cards linked as children of the completing task.
2027+
# Cheap: one query, indexed on parent_id.
2028+
linked_children: set[str] = set(child_ids(conn, completing_task_id))
2029+
20172030
verified: list[str] = []
20182031
phantom: list[str] = []
20192032
for cid in ordered:
20202033
created_by = found.get(cid)
20212034
if created_by is None:
20222035
phantom.append(cid)
20232036
continue
2024-
# Accept if created_by matches the completing task's assignee
2025-
# profile, OR the task itself (workers whose created_by happens
2026-
# to match their task id are unusual but harmless to accept).
2037+
# Accept if any of the three trust conditions holds.
20272038
if completing_assignee and created_by == completing_assignee:
20282039
verified.append(cid)
20292040
elif created_by == completing_task_id:
20302041
verified.append(cid)
2042+
elif cid in linked_children:
2043+
verified.append(cid)
20312044
else:
20322045
phantom.append(cid)
20332046
return verified, phantom

tests/hermes_cli/test_kanban_core_functionality.py

Lines changed: 40 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2978,6 +2978,46 @@ def test_complete_with_cross_worker_card_is_rejected(kanban_home):
29782978
conn.close()
29792979

29802980

2981+
def test_complete_accepts_cross_worker_card_when_linked_as_child(kanban_home):
2982+
"""A card created by a different principal but explicitly linked as
2983+
a child of the completing task is accepted — the worker took
2984+
ownership via ``kanban_create(parents=[current_task])`` or an
2985+
explicit ``link_tasks`` call, which proves the relationship even
2986+
when ``created_by`` doesn't match.
2987+
2988+
(Relaxation salvaged from #20022 @LeonSGP43 — stricter version
2989+
would incorrectly reject legitimate orchestrator flows where a
2990+
specifier creates a card, then a worker picks it up and links it
2991+
to its own parent task.)
2992+
"""
2993+
conn = kb.connect()
2994+
try:
2995+
parent = kb.create_task(conn, title="parent", assignee="alice")
2996+
# Card created by a DIFFERENT principal (not alice, not parent).
2997+
other = kb.create_task(
2998+
conn, title="other", assignee="x", created_by="bob",
2999+
parents=[parent], # explicitly links as child of the completing task
3000+
)
3001+
3002+
ok = kb.complete_task(
3003+
conn, parent,
3004+
summary="completed with linked child",
3005+
created_cards=[other],
3006+
)
3007+
assert ok is True
3008+
# The card should appear in the completed event's verified_cards list.
3009+
import json as _json
3010+
row = conn.execute(
3011+
"SELECT payload FROM task_events "
3012+
"WHERE task_id=? AND kind='completed' ORDER BY id DESC LIMIT 1",
3013+
(parent,),
3014+
).fetchone()
3015+
payload = _json.loads(row["payload"])
3016+
assert other in payload.get("verified_cards", [])
3017+
finally:
3018+
conn.close()
3019+
3020+
29813021
def test_complete_prose_scan_flags_nonexistent_ids(kanban_home):
29823022
"""Successful completion whose summary references a ``t_<hex>`` id
29833023
that doesn't resolve emits a ``suspected_hallucinated_references``

0 commit comments

Comments
 (0)