fix(free-tier): review round 2 — route-gate the allowance verdict, keep policy/billing 403s, pool the provision RPC, guard the retry race
Should-fix - _is_genuine_nous_rate_limit: the structured rate_limited verdict counts only on the welcome host; a paid-host 429 keeps main's exhausted-bucket rule. - _nous_welcome_tier: the route-keyed dark-tier 403 applies only to a 403 that matches neither the content-policy nor the billing patterns, so a safety refusal or billing wall on the welcome host keeps its own recovery. - free_tier.provision joins _LONG_HANDLERS (a forced mint + lock waits + re-inventory no longer block the RPC reader). - retry_bootstrap_mint: under the lock, a build that found no identity never overwrites a record that has one (the loop racing the user's click). Simplifications from the review - _raise_for_anon_status is a (status, error) table; retryable derives from ANON_TERMINAL_CODES once (a bare 401 on sign-up now rides the ladder instead of dying for the process). - classify_mint_exception is public and pure; the hand-built failure dict in free_tier.provision is gone (the memo is the one source). - SetupRecord carries the memo payload as one `failure` dict instead of three unpacked fields. - _welcome_surface_kind is a closed table with a "refused" default; _welcome_outage_copy excludes the classifier's `unknown` catch-all. - FREE_TIER_RATE_LIMIT_CHAT is CARD + the sign-in tail, not a slice. - Copy tests assert the contract (model named, tail present/absent) instead of freezing whole sentences. Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
This commit is contained in:
@@ -93,6 +93,7 @@ def test_provision_sets_the_free_tier_up_through_the_lifecycle_primitive(tmp_pat
|
||||
monkeypatch.setenv("HERMES_SHARED_AUTH_DIR", str(tmp_path / "shared-store"))
|
||||
monkeypatch.setenv("HERMES_GUEST_ONBOARDING", "1")
|
||||
calls = []
|
||||
real_ensure = anon_auth.ensure_portal_identity
|
||||
|
||||
def fake_provision(**kw):
|
||||
calls.append(kw)
|
||||
@@ -114,9 +115,13 @@ def test_provision_sets_the_free_tier_up_through_the_lifecycle_primitive(tmp_pat
|
||||
|
||||
with _auth_store_lock():
|
||||
store = _load_auth_store(); store["providers"].pop("nous"); _save_auth_store(store)
|
||||
monkeypatch.setattr(anon_auth, "ensure_portal_identity", refused)
|
||||
# The real primitive memoises the refusal; the RPC reports that memo.
|
||||
monkeypatch.setattr(anon_auth, "ensure_portal_identity", real_ensure)
|
||||
monkeypatch.setattr(anon_auth, "_reconcile_and_provision", refused)
|
||||
anon_auth.reset_mint_memo_for_tests()
|
||||
result = _call("free_tier.provision")
|
||||
assert result["has_guest"] is False and "not open" in result["error"]
|
||||
assert result["error_code"] == "anon_gate_closed" and result["retryable"] is False
|
||||
|
||||
_set_guest_off(monkeypatch)
|
||||
monkeypatch.setattr(anon_auth, "ensure_portal_identity", lambda **kw: (_ for _ in ()).throw(AssertionError("must not run")))
|
||||
|
||||
Reference in New Issue
Block a user