fix(kanban): promote refuses undone parents instead of a false --force success
`hermes kanban promote --force <id>` printed `Promoted <id> -> ready` and
then the very next claim (a human `claim`, or the dispatcher tick seconds
later) demoted the task back to `todo` with `claim_rejected
{parents_not_done}` and returned None (#106195). The non-force refusal
even pointed operators at `--force` as the escape hatch.
The claim gate is deliberate: `claim_task` is the single enforcement point
("never ready -> running with an undone parent, whichever writer set
'ready'", cda20eec0c), and `complete_task`/`request_review` re-check the
same predicate, so a child let through by a forced claim could still never
finish. A promotion override therefore has no honest outcome; the
dependency edge is the real knob.
- drop `--force` from `promote` (parser, CLI handler, `promote_task`
kwarg, the `forced` event field nothing read)
- the refusal message now states why the gate cannot be bypassed and names
the working remedies: complete the parents or `hermes kanban unlink`
- two invariant tests: refusal on an undone parent leaves `todo` with no
fake `ready`; the flag no longer parses
Salvage direction from #75354 by @vyacheslavk (diagnosis of the promote ->
claim gap); the consume-at-claim authorization there is not taken because
the same parent gate also blocks completion of the forced child.
This commit is contained in:
@@ -1013,13 +1013,13 @@ def _cmd_promote(args: argparse.Namespace) -> int:
|
||||
author = _profile_author()
|
||||
# Dedupe while preserving order; positional task_id always first.
|
||||
ids = list(dict.fromkeys(_bulk_ids(args)))
|
||||
dry_run, force = bool(args.dry_run), bool(args.force)
|
||||
dry_run = bool(args.dry_run)
|
||||
|
||||
results: list[dict[str, object]] = []
|
||||
with kbc.connect_closing() as conn:
|
||||
for tid in ids:
|
||||
ok, err = kb.promote_task(conn, tid, actor=author, reason=reason, force=force, dry_run=dry_run)
|
||||
results.append({"task_id": tid, "promoted": ok, "dry_run": dry_run, "forced": force,
|
||||
ok, err = kb.promote_task(conn, tid, actor=author, reason=reason, dry_run=dry_run)
|
||||
results.append({"task_id": tid, "promoted": ok, "dry_run": dry_run,
|
||||
"reason": reason, "error": err})
|
||||
|
||||
failed = [r for r in results if not r["promoted"]]
|
||||
|
||||
Reference in New Issue
Block a user