Quickstart: Using the Readiness Coordinator from a Subcommand
This quickstart shows the patterns downstream missions (WS2 auth, WS3 upgrade UX, WS5 tracker) will use to consume the coordinator's result from inside a subcommand handler.
Reading the cached result
import typer
from specify_cli.readiness import OutputPolicy, get_readiness
def my_subcommand(ctx: typer.Context) -> None:
readiness = get_readiness(ctx)
if not readiness.enabled:
# Hosted mode is off. Fall back to local-only behavior.
# Do NOT mention Teamspace in any output.
run_local_only()
return
if readiness.output_policy == OutputPolicy.MACHINE_OUTPUT:
# JSON / quiet path. Do not prompt. Emit structured output only.
emit_json(...)
return
if readiness.output_policy == OutputPolicy.NON_INTERACTIVE:
# Non-TTY / CI / help / version. Stable single-line stderr is OK.
# Prompts are forbidden.
emit_stderr_advisory(...)
run_local_only()
return
# Interactive path. Prompts and rich rendering permitted.
run_with_prompts()
Triggering the coordinator (this is the root callback's job)
This mission writes the only call site that triggers the coordinator. Downstream missions do NOT call evaluate_readiness; they call get_readiness.
# In src/specify_cli/cli/helpers.py callback() (after this mission's hook):
def callback(ctx: typer.Context) -> None:
# ... banner block ...
from specify_cli.readiness import evaluate_readiness
evaluate_readiness(ctx)
# ... maybe_emit_no_upgrade_notice block ...
The coordinator caches the result on ctx.obj["readiness"]. Subcommand handlers below the root callback read the cached result via get_readiness(ctx).
What this mission does NOT do (handed off to follow-ups)
- Auth probe: the
AuthStatus.NOT_CHECKEDvalue is a stub. WS2 wiresdetect_logged_out_with_connected_teamspaceinto the enabled path of_evaluate_uncached. - Upgrade UX prompts: snooze cadence, "Always keep me up to date", "Not now", "Never ask again" — WS3 extends
_invoke_nagwith these. - Tracker registration alignment: WS5 wires tracker commands through
get_readinessso hosted tracker paths share the same suppression contract.
Verifying the coordinator is wired
After this mission lands, the seam can be smoke-tested by:
# Hosted mode off — coordinator should be a no-op for all suppression rows.
unset SPEC_KITTY_ENABLE_SAAS_SYNC
spec-kitty --help 2>&1 | grep -i teamspace && echo "LEAK" || echo "ok"
spec-kitty --version 2>&1 | grep -i teamspace && echo "LEAK" || echo "ok"
# Hosted mode on — coordinator should run once per invocation.
export SPEC_KITTY_ENABLE_SAAS_SYNC=1
spec-kitty --help 2>&1 | grep -i teamspace && echo "LEAK (this is OK for help in hosted mode if/when WS2 adds it)" || echo "ok"
The 7-row suppression-matrix test under tests/readiness/test_coordinator_suppression_matrix.py is the authoritative check.