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 #20022 @LeonSGP43 (full PR superseded by #20232 +
this patch; the linked-children relaxation was the portable
improvement).
This commit is contained in:
LeonSGP43 2026-05-05 15:15:40 -07:00 committed by Teknium
parent eda326df16
commit 6d302b340e
2 changed files with 63 additions and 10 deletions

View file

@ -1978,14 +1978,23 @@ def _verify_created_cards(
) -> tuple[list[str], list[str]]:
"""Partition ``claimed_ids`` into (verified, phantom).
A card is "verified" iff a row exists in ``tasks`` with the given id
AND ``created_by`` matches the completing task's ``assignee`` (or
the completing task itself workers that create children of their
own task also qualify).
A card is "verified" iff a row exists in ``tasks`` AND at least one
of the following holds:
``phantom`` returns ids that either don't exist at all or exist but
were not created by the completing worker. The caller decides what
to do with each bucket; this helper never mutates.
* ``created_by`` matches the completing task's ``assignee`` profile
(the common case: worker A spawns a card via ``kanban_create``,
which stamps ``created_by=A``).
* ``created_by`` matches the completing task's id (edge case where
a worker passed its own task id as the ``created_by`` value).
* The card is linked as a ``task_links.child`` of the completing
task i.e. the worker explicitly called ``kanban_create`` with
``parents=[<current_task>]``. This accepts cards created through
the dashboard/CLI by a different principal but then attached to
the completing task by the worker.
``phantom`` returns ids that either don't exist at all, or exist
but don't satisfy any of the three trust conditions. The caller
decides what to do with each bucket; this helper never mutates.
"""
claimed = [str(x).strip() for x in (claimed_ids or []) if str(x).strip()]
if not claimed:
@ -2014,6 +2023,10 @@ def _verify_created_cards(
).fetchall()
found = {r["id"]: r["created_by"] for r in rows}
# Pull the set of cards linked as children of the completing task.
# Cheap: one query, indexed on parent_id.
linked_children: set[str] = set(child_ids(conn, completing_task_id))
verified: list[str] = []
phantom: list[str] = []
for cid in ordered:
@ -2021,13 +2034,13 @@ def _verify_created_cards(
if created_by is None:
phantom.append(cid)
continue
# Accept if created_by matches the completing task's assignee
# profile, OR the task itself (workers whose created_by happens
# to match their task id are unusual but harmless to accept).
# Accept if any of the three trust conditions holds.
if completing_assignee and created_by == completing_assignee:
verified.append(cid)
elif created_by == completing_task_id:
verified.append(cid)
elif cid in linked_children:
verified.append(cid)
else:
phantom.append(cid)
return verified, phantom