fix(insights): gate the ambient Coach routes on the Coach assistant matrix

The metric-page "Coach read" strip and the unscoped-hero seeded opener are
Coach surfaces, so both routes now call requireAssistantSurface("coach")
alongside the insights module gate. With the Coach turned off they return
the same refusal every other Coach route does, instead of lingering as a
"Coach read" line while the assistant is disabled. Restores the coach
route-gate inventory invariant.
This commit is contained in:
Marc-André Bombeck committed 2026-06-27 00:33:07 +02:00
1 parent ea1a0e2829
commit cbc428c89b
2 files changed
+11

No files matched your search

+6
View File
@@ -20,6 +20,7 @@ import { apiHandler, requireAuth } from "@/lib/api-handler";
import { annotate } from "@/lib/logging/context";
import { checkAnalyticsReadRateLimit } from "@/lib/rate-limit";
import { requireModuleEnabled } from "@/lib/modules/gate";
import { requireAssistantSurface } from "@/lib/feature-flags";
import { measurementTypeEnum } from "@/lib/validations/measurement";
import { buildCoachReadStrip } from "@/lib/insights/derived/coach-read";
import type { MeasurementType } from "@/generated/prisma/client";
@@ -36,6 +37,11 @@ export const GET = apiHandler(async (request: NextRequest) => {
const m = await requireModuleEnabled(user.id, "insights");
if (!m.enabled) return m.response;
// The strip is the ambient Coach presence on the metric page, so it
// gates on the Coach assistant matrix too: an operator who turns the
// Coach off must not see a "Coach read" line linger.
await requireAssistantSurface("coach");
// Shared analytics-read budget — generous; caps a runaway navigation loop.
const rl = await checkAnalyticsReadRateLimit(user.id);
if (!rl.allowed) {
@@ -22,6 +22,7 @@ import { apiHandler, requireAuth } from "@/lib/api-handler";
import { annotate } from "@/lib/logging/context";
import { checkAnalyticsReadRateLimit } from "@/lib/rate-limit";
import { requireModuleEnabled } from "@/lib/modules/gate";
import { requireAssistantSurface } from "@/lib/feature-flags";
import { prisma } from "@/lib/db";
import { loadBaselineProfile } from "@/lib/insights/derived";
import { detectDerivedBriefingSignals } from "@/lib/insights/derived-briefing";
@@ -50,6 +51,10 @@ export const GET = apiHandler(async () => {
const m = await requireModuleEnabled(user.id, "insights");
if (!m.enabled) return m.response;
// The opener feeds the Coach hero, so it gates on the Coach assistant
// matrix: with the Coach off there is no hero to seed.
await requireAssistantSurface("coach");
const rl = await checkAnalyticsReadRateLimit(user.id);
if (!rl.allowed) {
return apiError("Too many analytics requests. Please retry later.", 429);