
    def test_cli_flag_set_refuses_unregistered_name():
        from click.testing import CliRunner
>       from rtrader.cli.release_cli import cli

tests/services/test_feature_flags.py:251: 
_ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ 
rtrader/cli/release_cli.py:30: in <module>
    from rtrader.services import release_manager as WRM
rtrader/services/release_manager.py:64: in <module>
    from rtrader.services import release_manager_discord
rtrader/services/release_manager_discord.py:44: in <module>
    from rtrader.services import ack_briefing
_ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ 

    """Shared read-only INFO/MORE briefing renderer for the THREE Discord ack surfaces.
    
    Bug c24bb0ec01b1. The ack bots (#builder Crucible-ack, #release_manager GO/HOLD, #project_manager
    decisions) only accepted ``yes {code}`` / ``no {code}``. The operator often needs MORE context before
    deciding but had to leave Discord and dig. This module is the ONE renderer all three surfaces call for
    ``info {code}`` (aka ``more {code}``) — a READ-ONLY reply that never approves/rejects/merges anything.
    
    Per the operator's executable spec (bug c24bb0ec01b1, "TARGET OUTPUT" context note): gather FACTS
    mechanically from data already in hand (DiscoveredBugs.symptom + context_notes, CrucibleQueue fields, the
    commit subject/body via git, test names via ``git show <ref>:<file>``, pm2_restart_map.resolve_services,
    the live-path classifier's own fields) and quote them — **VERIFY, don't echo**. When a fact cannot be
    verified (no git access, no linked checkin), the briefing SAYS SO rather than omitting or guessing (CLAUDE.md:
    "if you don't know, say I don't know").
    
    Two-stage API, both pure/DI'd (no test ever touches real Mongo/git):
      - :func:`gather_facts` — reads DiscoveredBugs (symptom/severity/priority/status), the append-only
        ``context_notes`` story, dependency edges (:mod:`rtrader.services.bug_lifecycle`), the linked Crucible
        checkin fields (if any), an optional injected ``git`` callable for commit subject/body + test names,
        and an optional ``resolve_services_fn`` (:func:`rtrader.services.pm2_restart_map.resolve_services`).
      - :func:`format_briefing` — pure string formatter over the gathered facts (no I/O).
      - :func:`build_briefing` — the one-call convenience (gather + format) the bots call from
        ``briefing_formatter``.
    
    DOCTRINE (CLAUDE.md): every I/O boundary (Mongo, git, the PM2 service map) is dependency-injected;
    fail-fast on bug-class exceptions, fail-soft (Sentry + degrade) on operational ones; module-level imports
    only; the whole module is READ-ONLY (no write ever happens here).
    """
    from __future__ import annotations
    
    from dataclasses import dataclass, field
    from datetime import datetime
    from typing import Any, Callable, Dict, List, Optional
    
    import sentry_sdk
    
    from rtrader.config import settings
    from rtrader.services import bug_lifecycle
>   from rtrader.services.release_manager import GitFn, make_git
E   ImportError: cannot import name 'GitFn' from partially initialized module 'rtrader.services.release_manager' (most likely due to a circular import) (/Users/cao/robinhood-pm-b71083681d3c/rtrader/services/release_manager.py)

rtrader/services/ack_briefing.py:38: ImportError
