Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
8 changes: 2 additions & 6 deletions app/src/components/guidedStudy/SynthesisFreeRecap.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -5,17 +5,13 @@ import * as Sharing from 'expo-sharing';
import * as FileSystem from 'expo-file-system/legacy';
import { ChevronRight, Copy, Share2 } from 'lucide-react-native';
import { fontFamily, radii, spacing, useTheme } from '../../theme';
import { isFlagEnabled } from '../../config/featureFlags';
import type { CapturedInputs } from '../../services/guidedStudy/capturedInputs';
import type { CapturedTextRef } from '../../services/guidedStudy/stepBindings';
import { getCapturedText } from '../../services/guidedStudy/stepBindings';
import type { GuidedStudyMode } from '../../services/guidedStudy/types';
import { logger } from '../../utils/logger';

// Phase 3.1 (#1738) replaces this inline constant with an import from a
// dedicated feature-flags module. Until then the flag stays off so the
// premium-nudge footer copy reflects the structured (non-Amicus) path.
const GUIDED_STUDY_AMICUS_SYNTHESIS = false;

interface RecapSection {
label: string;
ref: CapturedTextRef;
Expand Down Expand Up @@ -152,7 +148,7 @@ export function SynthesisFreeRecap({

if (sections.length === 0) return null;

const footerCopy = GUIDED_STUDY_AMICUS_SYNTHESIS
const footerCopy = isFlagEnabled('GUIDED_STUDY_AMICUS_SYNTHESIS')
? PREMIUM_FOOTER_AMICUS_ON
: PREMIUM_FOOTER_AMICUS_OFF;

Expand Down
27 changes: 27 additions & 0 deletions app/src/config/__tests__/featureFlags.test.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,27 @@
import { FEATURE_FLAGS, isFlagEnabled, type FeatureFlag } from '../featureFlags';

describe('featureFlags', () => {
it('GUIDED_STUDY_AMICUS_SYNTHESIS defaults to false', () => {
expect(FEATURE_FLAGS.GUIDED_STUDY_AMICUS_SYNTHESIS).toBe(false);
expect(isFlagEnabled('GUIDED_STUDY_AMICUS_SYNTHESIS')).toBe(false);
});

it('FEATURE_FLAGS is exhaustively typed by FeatureFlag', () => {
// Round-trip through FeatureFlag — caller can iterate via a typed
// narrowing. If a flag is added to FEATURE_FLAGS without updating the
// type, tsc fails on this assignment.
const keys = Object.keys(FEATURE_FLAGS) as FeatureFlag[];
expect(keys).toContain('GUIDED_STUDY_AMICUS_SYNTHESIS');
for (const key of keys) {
expect(typeof FEATURE_FLAGS[key]).toBe('boolean');
}
});

it('isFlagEnabled returns the stored boolean', () => {
// Spot-check the helper actually reads from FEATURE_FLAGS rather than
// hardcoding a value.
expect(isFlagEnabled('GUIDED_STUDY_AMICUS_SYNTHESIS')).toBe(
FEATURE_FLAGS.GUIDED_STUDY_AMICUS_SYNTHESIS,
);
});
});
28 changes: 28 additions & 0 deletions app/src/config/featureFlags.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,28 @@
/**
* Feature flags for in-flight work that needs to ship behind a switch.
*
* These are compile-time constants on purpose — no remote config in this
* codebase yet. Flipping a flag is a single-line PR + EAS Update.
*
* Add a comment per flag explaining what to verify before flipping.
*/
export const FEATURE_FLAGS = {
/**
* Enables Amicus-drafted synthesis at the end of guided study sessions.
* Before flipping true, verify:
* - Epic #1446 (Amicus AI Study Partner) is past Phase 3 (chat working
* against the production worker).
* - The mode-specific system prompts in app/src/services/amicus/prompts/
* guidedStudy.ts have been reviewed.
* - Anthropic prompt caching is hitting on the system prompt (>80% cache
* rate observed in dev for at least 100 calls).
* When false, premium users get the structured-form synthesis path.
*/
GUIDED_STUDY_AMICUS_SYNTHESIS: false,
} as const;

export type FeatureFlag = keyof typeof FEATURE_FLAGS;

export function isFlagEnabled(flag: FeatureFlag): boolean {
return FEATURE_FLAGS[flag];
}
Loading