Remove silent fallbacks found in the 2026-10-03 audit

- /api/ai/ask: the stub answer is reachable only under DEBUG or
  RAG_FORCE_MOCK. Without RAG keys on the server it used to return 200 with
  "this is a stub, set OPENAI_API_KEY" and spend the student's quota; now 503.
- /api/home: the `grade` alias is gone and sending it is a 400 instead of a
  silent fallback to the user's own grade. No client sent it.
- activate_subscription logs ERROR when the paid order's plan no longer exists
  (it silently became 30 days).
- resolve_grade_title logs instead of `except: pass`; curriculum_io no longer
  reads an `intro_text` key no file contains.
- CLAUDE.md: "No silent fallbacks" rules for future agents.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
This commit is contained in:
2026-10-03 10:38:42 +03:30
parent 7e4b8b54a6
commit 7307bfcadb
10 changed files with 98 additions and 24 deletions

View File

@@ -587,7 +587,7 @@ class HomeViewScalabilityTests(TestCase):
def _home(self, grade_id):
return self.client.get(
f'/api/home?grade={grade_id}', HTTP_AUTHORIZATION=f'Bearer {self.token}')
f'/api/home?gradeId={grade_id}', HTTP_AUTHORIZATION=f'Bearer {self.token}')
def test_query_count_is_flat_regardless_of_node_count(self):
g_small = self._grade_with_nodes(2)
@@ -726,7 +726,7 @@ class ReadThrottleTests(TestCase):
codes = set()
for _ in range(122):
codes.add(self.client.get(
f'/api/home?grade={self.grade.id}',
f'/api/home?gradeId={self.grade.id}',
HTTP_AUTHORIZATION=f'Bearer {self.token}').status_code)
self.assertIn(429, codes)
@@ -774,7 +774,7 @@ class HomeGradeFallbackTests(TestCase):
self.user.grade_id = self.grade.id
self.user.save(update_fields=['grade_id'])
response = self._home(f'?grade={other.id}')
response = self._home(f'?gradeId={other.id}')
self.assertEqual(response.data['units'][0]['title'], 'فصل مثلثات')
@@ -785,7 +785,7 @@ class HomeGradeFallbackTests(TestCase):
self.assertEqual(response.data['units'], [])
def test_unknown_explicit_grade_is_still_404(self):
self.assertEqual(self._home('?grade=999999').status_code, 404)
self.assertEqual(self._home('?gradeId=999999').status_code, 404)
def test_a_deleted_saved_grade_degrades_to_an_empty_map(self):
# پایه‌ای که کاربر ذخیره کرده حذف شده — این خطای کاربر نیست.