"""AP2.2c — A2 sequential_dependency: Gate-Horizont + Agile-Komposition.""" from __future__ import annotations from tests.factories import provision_user_in_tenant from tests.test_initiatives_actions import _auth, _create_initiative, _login def _gate_ids(client, token, initiative_id): roadmap = client.get( f"/api/initiatives/{initiative_id}/roadmap/items", headers=_auth(token), ) assert roadmap.status_code == 200 items = roadmap.json() active = next(i for i in items if i.get("status") == "active") planned = next(i for i in items if i.get("title", "").startswith("G2")) return active["id"], planned["id"] def test_sequential_next_action_only_in_active_gate_horizon(client): user = provision_user_in_tenant(tenant_role="admin") token = _login(client, user) created = _create_initiative( client, token, title="Horizon Test", archetype_key="initiative.linear_project", ) initiative_id = created.json()["id"] g1_id, g2_id = _gate_ids(client, token, initiative_id) off_horizon = client.post( f"/api/initiatives/{initiative_id}/actions", json={ "title": "Außerhalb Horizont", "status": "open", "roadmap_item_id": g2_id, "sort_order": 0, }, headers=_auth(token), ) on_horizon = client.post( f"/api/initiatives/{initiative_id}/actions", json={ "title": "Im aktiven Gate", "status": "open", "roadmap_item_id": g1_id, "sort_order": 1, }, headers=_auth(token), ) assert off_horizon.status_code == 201 and on_horizon.status_code == 201 snap = client.get( f"/api/initiatives/{initiative_id}/steering-snapshot", headers=_auth(token), ) assert snap.status_code == 200 next_actions = snap.json().get("next_actions") or [] assert next_actions next_ids = {n.get("action_id") for n in next_actions} assert off_horizon.json()["id"] not in next_ids assert on_horizon.json()["id"] in next_ids def test_agile_on_linear_delegates_primary_without_active_sprint(client): user = provision_user_in_tenant(tenant_role="admin") token = _login(client, user) created = _create_initiative( client, token, title="Agile Primary", archetype_key="initiative.linear_project", ) initiative_id = created.json()["id"] g1_id, _ = _gate_ids(client, token, initiative_id) action = client.post( f"/api/initiatives/{initiative_id}/actions", json={ "title": "Gate Action", "status": "open", "roadmap_item_id": g1_id, }, headers=_auth(token), ) assert action.status_code == 201 snap = client.get( f"/api/initiatives/{initiative_id}/steering-snapshot", headers=_auth(token), ) assert snap.status_code == 200 assert snap.json().get("active_work_cycle") is None next_actions = snap.json().get("next_actions") or [] assert next_actions[0]["action_id"] == action.json()["id"] assert next_actions[0]["reason_code"] in ( "execution_ready", "execution_critical_path", ) def test_agile_on_linear_sprint_intersects_gate_horizon(client): user = provision_user_in_tenant(tenant_role="admin") token = _login(client, user) created = _create_initiative( client, token, title="Sprint Horizon", archetype_key="initiative.linear_project", ) initiative_id = created.json()["id"] g1_id, _ = _gate_ids(client, token, initiative_id) cycle = client.post( f"/api/initiatives/{initiative_id}/work-cycles", json={"title": "Montage-Woche", "status": "active"}, headers=_auth(token), ) assert cycle.status_code == 201 cycle_id = cycle.json()["id"] outside_sprint = client.post( f"/api/initiatives/{initiative_id}/actions", json={ "title": "Nur Gate", "status": "open", "roadmap_item_id": g1_id, }, headers=_auth(token), ) in_sprint = client.post( f"/api/initiatives/{initiative_id}/actions", json={ "title": "Im Sprint", "status": "ready", "roadmap_item_id": g1_id, "work_cycle_id": cycle_id, }, headers=_auth(token), ) assert outside_sprint.status_code == 201 and in_sprint.status_code == 201 snap = client.get( f"/api/initiatives/{initiative_id}/steering-snapshot", headers=_auth(token), ) assert snap.status_code == 200 assert snap.json().get("active_work_cycle") next_actions = snap.json().get("next_actions") or [] assert next_actions assert next_actions[0]["action_id"] == in_sprint.json()["id"] assert next_actions[0]["reason_code"] == "work_cycle_ready" def test_operating_context_active_composition_modifier_with_sprint(client): user = provision_user_in_tenant(tenant_role="member") token = _login(client, user) created = _create_initiative( client, token, title="Composition Flag", archetype_key="initiative.linear_project", ) initiative_id = created.json()["id"] ctx_before = client.get( f"/api/initiatives/{initiative_id}/operating-context", headers=_auth(token), ) assert ctx_before.json().get("active_composition_modifier") is None client.post( f"/api/initiatives/{initiative_id}/work-cycles", json={"title": "S1", "status": "active"}, headers=_auth(token), ) ctx_after = client.get( f"/api/initiatives/{initiative_id}/operating-context", headers=_auth(token), ) assert ctx_after.json().get("active_composition_modifier") == "agile_iteration"