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
6 changes: 4 additions & 2 deletions apps/browser/src/main/feature-bridge.ts
Original file line number Diff line number Diff line change
Expand Up @@ -165,8 +165,10 @@ ipcMain.handle(IPC_CHANNELS.applyLocalRecompose, async (_event, untrusted) => {
if (result.output === null) {
return localRecomposeResultSchema.parse({
...result,
// The deterministic interface is already active even when Qwen is not.
applied: true,
// `applied` describes the local-model refinement only. The deterministic
// Recompose surface remains active, but the UI must not claim Qwen
// successfully personalized the page when the local model failed.
applied: false,
});
}

Expand Down
33 changes: 32 additions & 1 deletion apps/browser/src/shared/recompose.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -31,7 +31,6 @@ describe('Recompose judge profiles', () => {
const steps = profileForRecomposePreset(original, 'step_by_step');

expect(calm.preferences.informationDensity).toBe('calm');
expect(calm.preferences.reduceMotion).toBe(true);
expect(visual.preferences.textScale).toBeGreaterThanOrEqual(1.35);
expect(visual.preferences.targetSizePx).toBe(60);
expect(motor.capabilities.motor).toBe('important');
Expand All @@ -40,6 +39,38 @@ describe('Recompose judge profiles', () => {
expect(steps.capabilities.cognitive).toBe('important');
});

it('preserves the actual reduced-motion preference across demo presets', () => {
const motionAllowed = createDefaultBrowserProfile(
'2026-07-24T00:00:00.000Z',
'profile-motion',
);
motionAllowed.preferences.reduceMotion = false;

for (const preset of [
'clear_calm',
'easier_to_see',
'easy_to_control',
'step_by_step',
] as const) {
expect(profileForRecomposePreset(motionAllowed, preset).preferences.reduceMotion).toBe(
false,
);
}

const reduced = structuredClone(motionAllowed);
reduced.preferences.reduceMotion = true;
for (const preset of [
'clear_calm',
'easier_to_see',
'easy_to_control',
'step_by_step',
] as const) {
expect(profileForRecomposePreset(reduced, preset).preferences.reduceMotion).toBe(
true,
);
}
});

it('keeps the personalized option identical in behavior but cloned', () => {
const original = createDefaultBrowserProfile(
'2026-07-24T00:00:00.000Z',
Expand Down
7 changes: 4 additions & 3 deletions apps/browser/src/shared/recompose.ts
Original file line number Diff line number Diff line change
Expand Up @@ -122,13 +122,16 @@ export function profileForRecomposePreset(
if (preset === 'personalized') return structuredClone(profile);

const next = structuredClone(profile);
// Judge presets demonstrate layout/information differences. They deliberately
// preserve the person's existing motion preference instead of silently
// forcing reduced motion, so the progressive Recompose transition remains
// visible unless the person has actually asked AURA to reduce motion.
if (preset === 'clear_calm') {
next.capabilities.attention = 'important';
next.preferences.explanationStyle = 'concise';
next.preferences.informationDensity = 'calm';
next.preferences.lineSpacing = Math.max(next.preferences.lineSpacing, 1.55);
next.preferences.readingWidth = 'narrow';
next.preferences.reduceMotion = true;
next.preferences.targetSizePx = Math.max(next.preferences.targetSizePx, 52);
next.preferences.textScale = Math.max(next.preferences.textScale, 1.1);
next.summary =
Expand All @@ -148,7 +151,6 @@ export function profileForRecomposePreset(
if (preset === 'easy_to_control') {
next.capabilities.motor = 'important';
next.preferences.informationDensity = 'calm';
next.preferences.reduceMotion = true;
next.preferences.strongFocus = true;
next.preferences.targetSizePx = 60;
next.preferences.textScale = Math.max(next.preferences.textScale, 1.08);
Expand All @@ -161,7 +163,6 @@ export function profileForRecomposePreset(
next.preferences.explanationStyle = 'concise';
next.preferences.informationDensity = 'step_by_step';
next.preferences.readingWidth = 'narrow';
next.preferences.reduceMotion = true;
next.preferences.strongFocus = true;
next.preferences.targetSizePx = Math.max(next.preferences.targetSizePx, 52);
next.preferences.textScale = Math.max(next.preferences.textScale, 1.1);
Expand Down
12 changes: 7 additions & 5 deletions docs/browser/12-RECOMPOSE-VOICE.md
Original file line number Diff line number Diff line change
Expand Up @@ -21,6 +21,8 @@ The event build exposes four non-diagnostic example profiles alongside the perso

These are demonstrations of needs, not medical modes. The fifth option is **My profile**, which uses Learn Me.

Judge presets change layout, density, target size, and information hierarchy, but they do not silently change the person's motion preference. Progressive Recompose transitions remain visible when motion is allowed; a Learn Me/system reduced-motion preference still removes spatial motion across every preset.

## Recompose architecture

```text
Expand Down Expand Up @@ -157,12 +159,12 @@ MediaRecorder
OpenAI transcription
transcript in composer
validated transcript
existing Talk to AURA request
```

The baseline implementation uses `gpt-4o-mini-transcribe` after the person stops recording because it is simple, low-cost, and uses the existing OpenAI key. A future enhancement may replace it with `gpt-realtime-whisper` transcript deltas without changing Talk to AURA.
The baseline implementation uses `gpt-4o-mini-transcribe` after the person stops recording because it is simple, low-cost, and uses the existing OpenAI key. The finalized transcript is submitted directly into the same Talk to AURA path as typed text, so the judge gets an immediate page/action response rather than an extra confirmation click. The transcribed text remains visible as the user's conversation message. A future enhancement may replace it with `gpt-realtime-whisper` transcript deltas without changing Talk to AURA.

Primary OpenAI references:

Expand Down Expand Up @@ -201,11 +203,11 @@ AURA is ready when all of the following are true:
- the same arbitrary page looks materially different under at least three judge presets;
- Fiverr/search/listing pages no longer remain a lightly restyled grid;
- the first useful visible change begins immediately after `Make This Mine`;
- local Qwen failure still leaves a convincing deterministic Recompose interface;
- local Qwen failure still leaves a convincing deterministic Recompose interface and is reported as a local fallback, not as a successful Qwen refinement;
- cloud failure still leaves the local/deterministic interface usable;
- `Original` restores the real website without reload and without losing form state;
- every Recompose action maps to a real current-page target;
- push-to-talk fills the existing Talk to AURA composer and can submit normally;
- push-to-talk submits the finalized transcript through the existing Talk to AURA path and exposes that transcript in conversation history;
- AURA can speak a short assistant reply and can be interrupted;
- reduced-motion preferences disable spatial Recompose transitions;
- reduced-motion preferences disable spatial Recompose transitions while motion-enabled profiles retain the progressive transition across judge presets;
- CI remains green for lint, typecheck, tests, build, and Electron E2E.