From e449a19e55e742239cf292377a1b4ac61bfc858b Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Marc-Andr=C3=A9=20Bombeck?= Date: Sun, 2 Aug 2026 18:17:05 +0200 Subject: [PATCH] docs(sharing): say how the cycle gate actually resolves the record MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit The two cycle entries in the frozen list claimed the gate reads `gender` off the resolved auth context. It does not: `requireCycleEnabled` names that parameter `_gender` and ignores it, then re-loads gender and the profile by the resolved user id through the module gate. The answer is the same — the owner's — but a reviewer checking the claim would have found a dead argument and no explanation. The profile route is the exception and now says so: it passes the auth-context gender to `isCycleEnabled` itself to compute the flag it returns. A frozen-list entry is only worth the reviewing it invites, so it has to describe the mechanism that is there rather than the one the call site looks like it has. --- src/__tests__/delegable-surface-guard.test.ts | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/__tests__/delegable-surface-guard.test.ts b/src/__tests__/delegable-surface-guard.test.ts index 603afeea5..7681bcfa3 100644 --- a/src/__tests__/delegable-surface-guard.test.ts +++ b/src/__tests__/delegable-surface-guard.test.ts @@ -312,11 +312,11 @@ const DELEGABLE_ROUTES: Record = { "app/api/workouts/route.ts": "The record's workout list. Verified to carry no AI-written paragraph: the single-workout route beside it does, and stays refused for exactly that reason.", "app/api/cycle/cycles/route.ts": - "The record's cycle history. The gate reads `user.gender` off the resolved record, which is the point: whether cycle tracking applies is a property of the record, not of whoever is looking at it.", + "The record's cycle history. Whether cycle tracking applies is a property of the record, and the gate gets there by re-loading the profile and gender for the resolved user id — note that the `gender` argument at the call site is dead (`requireCycleEnabled` names it `_gender`), so it is the id that carries the substitution, not that argument.", "app/api/cycle/day-logs/route.ts": 'One day of the record\'s cycle log. The `auditLog("cycle.day-log.upsert")` in this file belongs to the PUT, which keeps `requireAuth()`.', "app/api/cycle/profile/route.ts": - "The record's cycle profile, supplied by the gate itself. No query of its own.", + "The record's cycle profile, supplied by the gate itself. This is the one cycle route that does read the auth-context gender directly — `isCycleEnabled(user.gender, gate.profile)` computes the `resolved` flag it returns — and post-substitution that is the owner's.", "app/api/cycle/symptoms/custom/route.ts": "The record's own custom symptom vocabulary. The create arm carries the rate limit and keeps `requireAuth()`.", "app/api/medications/route.ts":