Research: Auth Local Trust And Multi-Process Hardening
Decision: Treat #977 As Hermetic Test Isolation First
Choice: Fix refresh-lock concurrency tests so their fake refreshed sessions do not trigger real hosted membership rehydrate when SPEC_KITTY_SAAS_URL is configured in the developer shell.
Rationale: The known diagnosis says TokenManager.refresh_if_needed() calls _apply_post_refresh_membership_hook() after refresh, and the fake sessions in tests/auth/concurrency/test_machine_refresh_lock.py lack a Private Teamspace. That makes the test accidentally call hosted /api/v1/me under inherited hosted URL configuration. The same test passes when the hosted URL is unset.
Alternatives considered:
- Change production refresh-lock behavior immediately: rejected because the evidence points to test isolation, not lock algorithm failure.
- Mark the test as hosted smoke: rejected because lock behavior should be hermetic default coverage.
- Clear every hosted env var globally for the whole suite: useful as a guard, but too broad as the only fix because the fixture should model the intended test boundary explicitly.
Decision: Keep Diagnostic Classification CLI-Owned Initially
Choice: Implement #829 and #889 in CLI command/sync/tracker-bound surfaces first, using existing categories where available.
Rationale: The visible failure is user-facing classification and guidance. Existing CLI modules already emit spec-kitty auth login, direct-ingress warnings, and server_error categories. There is no current evidence that spec-kitty-tracker owns the classification bug.
Alternatives considered:
- Create a tracker mission immediately: rejected because
start-here.mdsays tracker is context-only unless ownership is proven. - Add a new universal auth error enum: rejected unless investigation shows current categories cannot express the required cases.
Decision: Preserve Existing Direct-Ingress Missing-Private-Team Category
Choice: Use the existing direct_ingress_missing_private_team style category for Private Teamspace ingress rejection and prevent collapse into server_error.
Rationale: src/specify_cli/sync/_team.py already defines CATEGORY_MISSING_PRIVATE_TEAM = "direct_ingress_missing_private_team", and multiple sync tests already assert the direct-ingress warning/category. The requirement is to classify a 403/missing-private-team path consistently.
Alternatives considered:
- Introduce a differently named category: rejected because it would create migration risk and weaken existing tests.
- Treat all 403 responses as auth expired: rejected because missing Private Teamspace is an authorization/domain state, not necessarily an expired login.
Decision: BLE001 Guardrail Extends Existing Review Direction
Choice: Reuse or extract the current BLE001 audit pattern from src/specify_cli/cli/commands/review.py, scoped to auth/storage paths, and make it directly testable.
Rationale: The repository already treats unjustified BLE001 suppressions as review risk. This mission needs a stronger auth/storage guard, not a new lint ecosystem. The acceptance need is actionable output with file and line.
Alternatives considered:
- Rely on ruff alone: rejected because the mission allows broad catches only with an inline safety reason.
- Ban all broad catches in auth/storage: rejected because cleanup and diagnostic-translation boundaries can be legitimate when justified.
Decision: Hot Path Is Derived, Bounded, And Invalidatable
Choice: Any cross-process session hot-path cache or handoff must be derived from durable encrypted storage, invalidated when durable state changes, and safely bypassable.
Rationale: The current storage model is encrypted file-only and explicitly excludes Keychain/keyring/Secret Service. The performance issue is repeated local-process work, not lack of durable authority. A derived handoff can reduce work without changing trust authority.
Alternatives considered:
- Reintroduce OS credential managers: rejected by spec constraints and current packaging tests.
- Store raw token material in a plaintext cache: rejected because it weakens the local trust model and risks user-output leakage.
- Skip hot-path work entirely: rejected because SaaS #77 explicitly calls for CLI-side cross-process cache/handoff design.
Decision: Hosted Smoke Remains Explicit
Choice: Default tests remain hermetic; any hosted auth/tracker/sync smoke uses explicit command lines and, on this computer, SPEC_KITTY_ENABLE_SAAS_SYNC=1.
Rationale: The local machine rule exists for hosted testing, but it must not make normal test suites depend on https://spec-kitty-dev.fly.dev.
Alternatives considered:
- Let all tests use the dev deployment opportunistically: rejected because it caused #977 and makes tests flaky.
- Disable hosted URL configuration in all codepaths: rejected because explicit hosted smoke remains valuable.