mirror of
https://github.com/rennf93/roboco.git
synced 2026-08-03 07:23:24 +02:00
A MegaTask root-subtask can now target an ad-hoc per-cell project map — a
third targeting shape that mirrors the existing product fan-out root. In
RoboCo a project is per-cell (ProjectTable.assigned_cell); a monorepo is N
per-cell projects sharing one git_url. So 'multi-cell' IS 'multi-project',
and a task may mix per-cell projects across products or include OSS-library
projects not in any product.
Storage: migration 052 adds task_cell_projects (mirrors product_projects;
unique per (task, team)). TaskTable gains a cascade-delete cell_projects
relationship; TaskCreateRequest / TaskCreate / Task response carry the map.
Policy: batch.is_branchless_coordination + is_valid_batch_shape gain a
has_cell_projects param — a root-subtask targets exactly one of project /
product / cell-map; the umbrella still targets none. TaskService passes
has_cell_projects at every predicate call site and persists the rows in
create(). _ensure_branch_for_task cuts feature/main_pm/{root} per distinct
project in the map (via _distinct_projects_for_task); _require_target_or_umbrella
and _validate_batch_membership accept the map shape.
Fan-out: every distinct_project_ids site (task.py branch creation, routes
_project_for_complete + _resolve_project_for_merge, orchestrator
_ambient_projects_for_task, pr_review._project_slug_for, git._project_for_task)
generalizes to first-distinct-project-of-map-or-product. Choreographer
_resolve_subtask_project resolves a delegated subtask's cell from the parent's
cell map. The product-scoped _slugs_for_product intake helper is unchanged.
Intake: prompter._draft_cell_map extracts the per-cell map from the_work[].
_validate_batch_scope counts distinct projects across all drafts' cells
(>=2 min stays; one 2-cell draft satisfies it). create_task_from_draft
persists cell_projects for >=2-cell drafts (project_id/product_id None),
collapses a 1-cell map to the single-project shape, and leaves single-cell
top-level project_id drafts unchanged. _resolve_owning_team routes a
multi-cell map to Main PM (coordination root, like a product root — a cell
PM can't delegate cross-cell). propose_draft/propose_batch tool descriptions
declare the per-cell project_id (both Claude SDK + grok runtimes).
The umbrella stays branchless / pure-coordination / submit_root-rejected;
the CEO-escalation pr_number gate is not widened (the map root is
is_umbrella=False, mirroring a product root, so submit_root supplies it).
Single-cell root-subtasks and everything below them are byte-for-byte
unchanged. Un-run MegaTask waves (multi-cell drafts) become runnable.
166 lines
6.3 KiB
Python
166 lines
6.3 KiB
Python
"""MegaTask identity + branchless-coordination predicates (the single source of
|
|
truth the orchestrator / git-gate / branch-creation / reject-routing consult)."""
|
|
|
|
from __future__ import annotations
|
|
|
|
from uuid import uuid4
|
|
|
|
from roboco.foundation.policy.batch import (
|
|
is_batch_root_subtask,
|
|
is_batch_umbrella,
|
|
is_branchless_coordination,
|
|
is_valid_batch_shape,
|
|
)
|
|
|
|
|
|
def test_umbrella_is_batch_id_set_and_top_level() -> None:
|
|
bid = uuid4()
|
|
assert is_batch_umbrella(batch_id=bid, parent_task_id=None)
|
|
assert not is_batch_umbrella(batch_id=bid, parent_task_id=uuid4()) # a child
|
|
assert not is_batch_umbrella(batch_id=None, parent_task_id=None) # a normal root
|
|
|
|
|
|
def test_root_subtask_is_batch_id_set_and_parented() -> None:
|
|
bid = uuid4()
|
|
assert is_batch_root_subtask(batch_id=bid, parent_task_id=uuid4())
|
|
assert not is_batch_root_subtask(batch_id=bid, parent_task_id=None) # the umbrella
|
|
assert not is_batch_root_subtask(batch_id=None, parent_task_id=uuid4())
|
|
|
|
|
|
def test_branchless_coordination_covers_product_root_and_umbrella() -> None:
|
|
# product fan-out coordination root: no project, carries a product
|
|
assert is_branchless_coordination(project_id=None, product_id=uuid4())
|
|
# MegaTask umbrella: batch_id set, top-level
|
|
assert is_branchless_coordination(
|
|
project_id=None, product_id=None, batch_id=uuid4(), parent_task_id=None
|
|
)
|
|
|
|
|
|
def test_branchless_coordination_excludes_normal_and_root_subtasks() -> None:
|
|
# a normal project task does its own git
|
|
assert not is_branchless_coordination(project_id=uuid4(), product_id=None)
|
|
# a root-subtask (has a parent + a project) is NOT the branchless umbrella
|
|
assert not is_branchless_coordination(
|
|
project_id=uuid4(),
|
|
product_id=None,
|
|
batch_id=uuid4(),
|
|
parent_task_id=uuid4(),
|
|
)
|
|
# genuinely unroutable (none of project / product / batch / cell-map) stays gated
|
|
assert not is_branchless_coordination(project_id=None, product_id=None)
|
|
|
|
|
|
def test_branchless_coordination_covers_ad_hoc_cell_map_root() -> None:
|
|
# An ad-hoc per-cell project map (no project_id, no product_id, carries a
|
|
# cell map) is a coordination root exactly like a Product fan-out root: it
|
|
# cuts feature/main_pm/{root} per repo and opens a root->master PR per repo,
|
|
# so the claim branch gate skips the single-branch requirement. Holds both
|
|
# for a MegaTask root-subtask and a standalone coordination root.
|
|
assert is_branchless_coordination(
|
|
project_id=None, product_id=None, has_cell_projects=True
|
|
)
|
|
assert is_branchless_coordination(
|
|
project_id=None,
|
|
product_id=None,
|
|
batch_id=uuid4(),
|
|
parent_task_id=uuid4(),
|
|
has_cell_projects=True,
|
|
)
|
|
# a cell map is NOT branchless if a project_id is also set (then it's a normal
|
|
# project task that happens to carry a stray map — gated, not exempt).
|
|
assert not is_branchless_coordination(
|
|
project_id=uuid4(), product_id=None, has_cell_projects=True
|
|
)
|
|
|
|
|
|
def test_valid_batch_shape_allows_umbrella_and_root_subtask() -> None:
|
|
bid = uuid4()
|
|
# umbrella: batch_id, no parent, NO target
|
|
assert is_valid_batch_shape(
|
|
batch_id=bid, parent_task_id=None, project_id=None, product_id=None
|
|
)
|
|
# root-subtask: batch_id, a parent, exactly one target (project)
|
|
assert is_valid_batch_shape(
|
|
batch_id=bid, parent_task_id=uuid4(), project_id=uuid4(), product_id=None
|
|
)
|
|
# root-subtask targeting a product instead is also well-formed
|
|
assert is_valid_batch_shape(
|
|
batch_id=bid, parent_task_id=uuid4(), project_id=None, product_id=uuid4()
|
|
)
|
|
# no batch_id → unconstrained here
|
|
assert is_valid_batch_shape(
|
|
batch_id=None, parent_task_id=None, project_id=uuid4(), product_id=None
|
|
)
|
|
|
|
|
|
def test_valid_batch_shape_denies_stray_batch_id() -> None:
|
|
bid = uuid4()
|
|
# an umbrella-shaped task (batch_id, no parent) that ALSO targets a project —
|
|
# the spoof that would otherwise get the branchless exemption — is refused.
|
|
assert not is_valid_batch_shape(
|
|
batch_id=bid, parent_task_id=None, project_id=uuid4(), product_id=None
|
|
)
|
|
# umbrella with a product is equally malformed
|
|
assert not is_valid_batch_shape(
|
|
batch_id=bid, parent_task_id=None, project_id=None, product_id=uuid4()
|
|
)
|
|
# a root-subtask (has a parent) with NO target is malformed
|
|
assert not is_valid_batch_shape(
|
|
batch_id=bid, parent_task_id=uuid4(), project_id=None, product_id=None
|
|
)
|
|
# a root-subtask with BOTH targets is malformed
|
|
assert not is_valid_batch_shape(
|
|
batch_id=bid, parent_task_id=uuid4(), project_id=uuid4(), product_id=uuid4()
|
|
)
|
|
|
|
|
|
def test_valid_batch_shape_allows_ad_hoc_cell_map_root_subtask() -> None:
|
|
bid = uuid4()
|
|
# a root-subtask carrying an ad-hoc per-cell map (no project, no product) is a
|
|
# well-formed third targeting shape — exactly one of {project, product, map}.
|
|
assert is_valid_batch_shape(
|
|
batch_id=bid,
|
|
parent_task_id=uuid4(),
|
|
project_id=None,
|
|
product_id=None,
|
|
has_cell_projects=True,
|
|
)
|
|
# a non-batch task carrying a cell map is unconstrained here (the normal
|
|
# targeting rule applies; the map is a coordination-root shape in its own
|
|
# right, not a batch-only construct).
|
|
assert is_valid_batch_shape(
|
|
batch_id=None,
|
|
parent_task_id=None,
|
|
project_id=None,
|
|
product_id=None,
|
|
has_cell_projects=True,
|
|
)
|
|
|
|
|
|
def test_valid_batch_shape_denies_cell_map_alongside_another_target() -> None:
|
|
bid = uuid4()
|
|
# umbrella with a cell map: an umbrella must target NEITHER — a map is a target.
|
|
assert not is_valid_batch_shape(
|
|
batch_id=bid,
|
|
parent_task_id=None,
|
|
project_id=None,
|
|
product_id=None,
|
|
has_cell_projects=True,
|
|
)
|
|
# root-subtask with BOTH a project and a cell map: two targets, malformed.
|
|
assert not is_valid_batch_shape(
|
|
batch_id=bid,
|
|
parent_task_id=uuid4(),
|
|
project_id=uuid4(),
|
|
product_id=None,
|
|
has_cell_projects=True,
|
|
)
|
|
# root-subtask with BOTH a product and a cell map: two targets, malformed.
|
|
assert not is_valid_batch_shape(
|
|
batch_id=bid,
|
|
parent_task_id=uuid4(),
|
|
project_id=None,
|
|
product_id=uuid4(),
|
|
has_cell_projects=True,
|
|
)
|