"""Which rules a caller may be shown — a rule's HOME, stated once (milestone 414). A rule lives in a rulebook topic — GLOBAL, it applies wherever its owner works — or on one project, where it applies to that project and nowhere else. Every read that hands rules to a session answers the same question about that home, so it is answered here and nowhere else: - `project_id` unset: global rules only. A caller that does not say gets the safe failure, which is surfacing less rather than another project's rules. - `project_id=N`: global rules plus project N's own, and N's only when the caller can read that project (access.can_read_project, so a shared project's rules reach its collaborators too). - `everywhere=True`: every rule the caller owns, in any home — the explicit whole-rulebook question. It was written inline in `semantic_search_rules` until the moment lookup (milestone 458) needed the same answer. Two copies of a scope clause is how one of them starts surfacing another project's rules, so it moved here first. """ from __future__ import annotations from sqlalchemy import or_ from scribe.services.access import can_read_project async def rule_home(user_id: int, project_id: int | None = None, *, everywhere: bool = False): """The WHERE clause for the rules this caller may be shown here. Needs the statement joined through `joined_to_homes`, which outer-joins the three tables the clause reads. A rule is in a topic XOR on a project (migration 0059), so it matches exactly one arm of whichever clause applies. """ from scribe.models.project import Project from scribe.models.rulebook import Rule, Rulebook global_rule = Rulebook.owner_user_id == user_id if everywhere: return or_(global_rule, Project.user_id == user_id) if project_id and await can_read_project(user_id, project_id): return or_(global_rule, Rule.project_id == project_id) return global_rule def joined_to_homes(stmt): """Outer-join a statement over `Rule` to the tables `rule_home` reads.""" from scribe.models.project import Project from scribe.models.rulebook import Rule, Rulebook, RulebookTopic return ( stmt.outerjoin(RulebookTopic, Rule.topic_id == RulebookTopic.id) .outerjoin(Rulebook, RulebookTopic.rulebook_id == Rulebook.id) .outerjoin(Project, Rule.project_id == Project.id) )