Tasks: CLI Startup Readiness Coordinator Skeleton

Mission: cli-startup-readiness-coordinator-skeleton-01KS7JRV Mission ID: 01KS7JRVSFFBWPD2XZ7B8162E6 Branch: kitty/mission-cli-startup-readiness-coordinator-skeleton-01KS7JRVmain Spec: spec.md | Plan: plan.md Total WPs: 3 | Total subtasks: 17


Work Package Overview

WPTitleDependenciesFRs coveredSubtasksagent_profile
WP01Coordinator package + callback hookFR-001, FR-002, FR-003, FR-004, FR-005, FR-006, FR-007, FR-008, FR-009, FR-010T001–T010python-pedro
WP02Suppression matrix + caching testsWP01FR-009, FR-011, FR-012, NFR-002, NFR-004T011–T013python-pedro
WP03Nag passthrough test + final verificationWP02FR-006, FR-012, NFR-001, NFR-004, NFR-005T014–T017python-pedro

All WPs run sequentially on a single lane (lane-a). There is no useful parallelism because WP02 tests the surface WP01 lands, and WP03 verifies the cross-cutting invariants over the surface WP02 covers.

WP01 is intentionally a single WP rather than a scaffold+wiring split because the file-ownership validator (finalize-tasks --validate-only) requires non-overlapping owned_files, and a clean split with coordinator.py in WP01 (scaffold) + coordinator.py in WP02 (body) would overlap.


FR Coverage Matrix

FRStatement (summary)WP(s)
FR-001src/specify_cli/readiness/ package exports evaluate_readiness, ReadinessResult, get_readinessWP01
FR-002evaluate_readiness(ctx) called once from root callback() after suppression conditions knownWP01
FR-003First gate is is_saas_sync_enabled(); no-op when disabledWP01
FR-004output_policy field derived from existing suppression signalsWP01
FR-005Auth-readiness stub field; _auth_recovery imported but not exercisedWP01
FR-006Coordinator wraps _render_nag_if_needed byte-for-byteWP01 (impl), WP03 (verification)
FR-007Result stored on ctx.obj["readiness"]WP01
FR-008get_readiness(ctx) returns cached or no-op default; never raises, never re-runsWP01 (impl), WP02 (tests)
FR-009Double-invocation returns cached resultWP01 (impl), WP02 (tests)
FR-010Coordinator never raises; exceptions → _NOOP_DISABLEDWP01 (impl), WP03 (verification)
FR-011No Teamspace string in stdout/stderr when hosted mode disabled, across the 7-row suppression matrixWP02
FR-012Tests under tests/readiness/ cover 7-row matrix and once-per-invocation invariantWP02, WP03
NFR-001Coordinator overhead ≤ 1ms p50 disabled / ≤ 2ms p50 enabledWP03 (verification by inspection)
NFR-002No network I/O in startup pathWP03 (verification via inspection / mock)
NFR-003No new disk reads beyond wrapped nagWP03 (verification by inspection)
NFR-004≥ 90% line coverage on specify_cli/readiness/WP03
NFR-005mypy --strict passes on new + modified filesWP01, WP03

Subtask Index

IDDescriptionWPParallel
T001Create src/specify_cli/readiness/__init__.py with public re-exportsWP01
T002Create src/specify_cli/readiness/coordinator.py types (OutputPolicy, AuthStatus, ReadinessResult, _NOOP_DISABLED)WP01
T003Implement _derive_output_policyWP01
T004Implement _read_cached / _write_cachedWP01
T005Implement _invoke_nag (lazy import of _render_nag_if_needed)WP01
T006Implement _evaluate_uncached (disabled + enabled paths)WP01
T007Implement public evaluate_readiness (cache + try/except)WP01
T008Implement public get_readinessWP01
T009Modify helpers.py callback() to call evaluate_readiness(ctx) in place of inline _render_nag_if_needed(ctx)WP01
T010Verify mypy --strict, imports resolve, WS2: auth probe wiring marker present, existing CI-determinism tests pass smokeWP01
T011Create tests/readiness/__init__.py package markerWP02
T012Write tests/readiness/test_coordinator_suppression_matrix.py: 7-row + 1-enabled parameterized matrix asserting output_policy, enabled, ran, and the no-Teamspace-leakage invariantWP02
T013Write tests/readiness/test_coordinator_caching.py: 5 cases (A–E) covering hosted-on cache, hosted-off cache, get_readiness identity, fresh-ctx default, non-dict ctx.obj defaultWP02
T014Write tests/readiness/test_coordinator_nag_passthrough.py: 3 cases (A: ALLOW_WITH_NAG renders to stderr; B: --json suppresses; C: planner-exception swallowed)WP03
T015Run pytest -q tests/cli_gate/test_ci_determinism.py and confirm it passes unchanged (formal gate for FR-006)WP03
T016Run mypy --strict src/specify_cli/readiness/ src/specify_cli/cli/helpers.py and confirm pass (NFR-005)WP03
T017Run pytest -q --cov=src/specify_cli/readiness tests/readiness/ and confirm ≥ 90% line coverage (NFR-004); run git diff main...HEAD --stat and confirm the diff is confined to the declared surfaces (AC #9)WP03

Work Packages

WP01 — Coordinator package + callback hook

Goal: Land the production code in one coherent change. Create src/specify_cli/readiness/ with __init__.py and coordinator.py. Implement all coordinator helpers and the public API. Wire evaluate_readiness(ctx) into the root callback in src/specify_cli/cli/helpers.py in place of the existing inline _render_nag_if_needed(ctx) call.

Priority: Critical. This is the seam landing.

Independent test: python -c "from specify_cli.readiness import AuthStatus, OutputPolicy, ReadinessResult, evaluate_readiness, get_readiness; print('OK')" succeeds; mypy --strict src/specify_cli/readiness/ src/specify_cli/cli/helpers.py passes; pytest -q tests/cli_gate/test_ci_determinism.py passes (smoke check).

Estimated prompt size: ~700 lines (10 subtasks × ~65 lines plus header/context). Within the maximum-size guideline because each subtask has tight, code-snippet-anchored guidance.

Included subtasks:

  • ✅ T001 Create src/specify_cli/readiness/__init__.py
  • ✅ T002 Create coordinator.py types
  • ✅ T003 Implement _derive_output_policy
  • ✅ T004 Implement _read_cached / _write_cached
  • ✅ T005 Implement _invoke_nag (lazy)
  • ✅ T006 Implement _evaluate_uncached (disabled + enabled paths)
  • ✅ T007 Implement public evaluate_readiness
  • ✅ T008 Implement public get_readiness
  • ✅ T009 Modify helpers.py callback() hook
  • ✅ T010 Verification: mypy, imports, smoke

Prompt: tasks/WP01-coordinator-package-and-hook.md


WP02 — Suppression matrix + caching tests

Goal: Prove the suppression contract (FR-011) and the once-per-invocation caching invariant (FR-009, part of FR-012) under test.

Priority: Verification. Locks in the FR-011 / FR-012 / NFR-002 contracts.

Independent test: pytest -q tests/readiness/test_coordinator_suppression_matrix.py tests/readiness/test_coordinator_caching.py is green.

Estimated prompt size: ~320 lines.

Included subtasks:

  • ✅ T011 Create tests/readiness/__init__.py
  • ✅ T012 Write the 7-row + 1-enabled suppression-matrix parameterized test
  • ✅ T013 Write the 5-case caching test

Dependencies: WP01.

Prompt: tasks/WP02-suppression-matrix-and-caching-tests.md


WP03 — Nag passthrough test + final verification

Goal: Prove nag passthrough is byte-for-byte preserved (FR-006), assert the existing CI-determinism tests still pass (AC #5), confirm coverage and mypy gates, and confirm the diff scope (AC #9).

Priority: Acceptance gate.

Independent test: pytest -q tests/readiness/ tests/cli_gate/test_ci_determinism.py is green; mypy --strict src/specify_cli/readiness/ src/specify_cli/cli/helpers.py passes; pytest --cov=src/specify_cli/readiness tests/readiness/ reports ≥ 90% line coverage; git diff main...HEAD --stat matches AC #9.

Estimated prompt size: ~300 lines.

Included subtasks:

  • ✅ T014 Write nag-passthrough tests (3 cases)
  • ✅ T015 Verify existing CI-determinism tests pass unchanged
  • ✅ T016 Verify mypy --strict passes for both modified module surfaces
  • ✅ T017 Verify coverage ≥ 90% and diff scope matches AC #9

Dependencies: WP02.

Prompt: tasks/WP03-nag-passthrough-and-final-verification.md


Parallelization Opportunities

None within this mission. All WPs sequential on lane-a.

MVP Scope

WP01 delivers the seam. WP02 + WP03 lock the contract under test. The mission requires all three.

Branch Strategy (re-stated)

  • Planning base branch: main
  • Mission branch: kitty/mission-cli-startup-readiness-coordinator-skeleton-01KS7JRV (this branch carries the planning artifacts and all WP commits)
  • Final merge target for the PR: main
  • Per-WP lane workspace: .worktrees/cli-startup-readiness-coordinator-skeleton-01KS7JRV-mid8-lane-a/ (resolved at spec-kitty implement time)