Skip to content
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
28 changes: 27 additions & 1 deletion linear/src/engine.py
Original file line number Diff line number Diff line change
Expand Up @@ -433,7 +433,33 @@ def on_linear_ready_gate(self, issue: Dict[str, Any]) -> List[Action]:
Action("linear", "set_status", issue_key, {"status": self.cfg.backlog_status}),
]

return []
# At this point the issue has a valid classifier intake + canonical type.
# Surface routing defaults with minimal safe automation.
qa_route, pm_route, route_reason = self.classify_route(actual_type, intake)
out: List[Action] = [
Action(
"linear",
"comment",
issue_key,
{
"body": (
"Routing recommendation (from `linear_type_classifier_v1.json`): "
f"QA={qa_route}, PM={pm_route}. Reason: {route_reason}"
)
},
)
]
if qa_route == "skip":
out.insert(
0,
Action(
"linear",
"set_label",
issue_key,
{"group": self.cfg.qa_gate_group, "value": self.cfg.qa_skipped},
),
)
return out

def on_linear_comment(
self,
Expand Down
16 changes: 15 additions & 1 deletion linear/tests/test_engine.py
Original file line number Diff line number Diff line change
Expand Up @@ -78,7 +78,21 @@ def test_ready_gate_accepts_emoji_type_label(self):
"description": "Linear Classification\n- Output: code\n- Behavior change: yes\n- Broken existing behavior: no\n- Evidence: acceptance criteria\n- Children expected: no\n- PM-testable: yes\n\nObjective\nScope\nRequired outputs\nVerification plan\nRollback note\nAcceptance / closure criteria",
}
actions = self.bot.on_linear_ready_gate(issue)
self.assertEqual(actions, [])
self.assertTrue(any(a.kind == "comment" and "Routing recommendation" in a.payload["body"] for a in actions))
self.assertFalse(any(a.kind == "set_status" for a in actions))
self.assertFalse(any(a.kind == "set_label" and a.payload.get("value") == "qa-skipped" for a in actions))

def test_ready_gate_design_autosets_qa_skipped(self):
issue = {
"identifier": "BIT-45",
"status": "Ready",
"labels": ["🎨 Design"],
"estimate": 3,
"description": "Linear Classification\n- Output: design artifact\n- Behavior change: no\n- Broken existing behavior: no\n- Evidence: design acceptance\n- Children expected: no\n- PM-testable: yes\n\nObjective\nScope\nRequired outputs\nVerification plan\nRollback note\nAcceptance / closure criteria",
}
actions = self.bot.on_linear_ready_gate(issue)
self.assertTrue(any(a.kind == "set_label" and a.payload.get("value") == "qa-skipped" for a in actions))
self.assertTrue(any(a.kind == "comment" and "QA=skip" in a.payload["body"] for a in actions))

def test_ready_gate_plan_parent_requires_estimate(self):
issue = {
Expand Down
Loading