Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
42 commits
Select commit Hold shift + click to select a range
8f519be
Collapse PTC to exclusive-only: single PTC experiment + RLM sub-exper…
ThomasK33 Aug 25, 2026
6bada51
Address Codex review round 1
ThomasK33 Aug 25, 2026
55bb8fd
Address Codex review round 2: legacy taskExperiments/localStorage ali…
ThomasK33 Aug 25, 2026
050a990
Address Codex review round 3: runtime config alias, persisted-state m…
ThomasK33 Aug 25, 2026
b1f8f35
Address Codex review round 4: exempt persistence/media results from k…
ThomasK33 Aug 25, 2026
b6308a3
Address Codex review round 5: taskExperiments downgrade mirror, null-…
ThomasK33 Aug 25, 2026
9d02950
Address Codex review round 6: capture-time mixed-media sanitizer, cod…
ThomasK33 Aug 25, 2026
55f033b
Fix lint: redundant union constituent, prefer optional chain
ThomasK33 Aug 25, 2026
14f6d36
Address Codex review round 7: retry-snapshot experiment aliases, capt…
ThomasK33 Aug 25, 2026
746d04d
review r8: capture-bounding hardening (args path retention, serialize…
ThomasK33 Aug 25, 2026
c7eb4e3
review r9: sanitize classic outer return + console media at capture; …
ThomasK33 Aug 25, 2026
247bd12
review r10: alias legacy PTC in follow-up dispatch; deep-walk outer m…
ThomasK33 Aug 25, 2026
f6cbc41
review r11: validate media types (well-formed + length) in isSupporte…
ThomasK33 Aug 25, 2026
7d7b2aa
review r12: depth-bound nested tool-record extraction; compact captur…
ThomasK33 Aug 25, 2026
c5a7d9c
review r13: replace over-depth nested tool subtrees with a bounded pl…
ThomasK33 Aug 25, 2026
df522f0
review r14: admit only string diffs from untrusted history (nested re…
ThomasK33 Aug 25, 2026
badc35e
review r15: deep-walk wrapper objects in request-time media extractio…
ThomasK33 Aug 25, 2026
f98b424
review r16: keep media-free deep JSON untouched in extraction; requir…
ThomasK33 Aug 25, 2026
01b36da
review r16b: walk wrapper siblings alongside nested tool-record extra…
ThomasK33 Aug 25, 2026
8f86bde
review r17: scan generic wrapper spans iteratively so deep-wrapped me…
ThomasK33 Aug 26, 2026
b4ae00b
Merge origin/main (jsonSafeClone code_execution normalization) into p…
ThomasK33 Aug 26, 2026
e505188
review r18: reject nested agent_skill_read records with explicit ok:f…
ThomasK33 Aug 26, 2026
86a8b11
Merge origin/main (CLI rlm-mode forwarding) into ptc-mode-w7we
ThomasK33 Aug 26, 2026
a36d144
review r18 retry: traverse non-media content parts for nested media c…
ThomasK33 Aug 26, 2026
6c86bc8
review r19: deep media exemption search + shared classic-mode capture…
ThomasK33 Aug 26, 2026
77479e9
review r20: restrict media exemption to nested extractable content co…
ThomasK33 Aug 26, 2026
1879aee
review r21: compute skill-body truncation budget in serialized space
ThomasK33 Aug 26, 2026
b4fc1ec
review r22: code-point-boundary skill truncation search + classic arg…
ThomasK33 Aug 26, 2026
c38ee0b
review r23: retain empty-prefix skill packages + sanitize standalone …
ThomasK33 Aug 26, 2026
2b551d6
lint: accept unknown in sanitizeStandaloneMediaPart (no-object-parame…
ThomasK33 Aug 26, 2026
bb8f6aa
review r24: zero-budget skill retention, metadata-agnostic leaf recog…
ThomasK33 Aug 26, 2026
ffbe42b
review r25: type-check optional filenames + iterative media graph walk
ThomasK33 Aug 26, 2026
13411f8
review r26: unbounded exemption scan, result-less edit truncation sig…
ThomasK33 Aug 26, 2026
f5668ef
review r27 (security): charge media-bearing sanitized values against …
ThomasK33 Aug 26, 2026
463b7a9
review r27: recency bump for result-less edits, media exemption beats…
ThomasK33 Aug 26, 2026
fc85b81
review r28: charge overflow markers to the shared budget, request-wid…
ThomasK33 Aug 26, 2026
4568982
review r29: shared budget for outer return/console args, success bit …
ThomasK33 Aug 26, 2026
8f0637c
review r30: global placeholder coalescing, exhaustion stub stops rete…
ThomasK33 Aug 26, 2026
c126c92
review r31: excess-placeholder coalescing (dedup case), existing conv…
ThomasK33 Aug 26, 2026
85d6fb4
review r32: unbounded iterative placeholder coalescing (deep generic …
ThomasK33 Aug 26, 2026
08ece9c
review r33: positive success for inline read records, init-time legac…
ThomasK33 Aug 26, 2026
6e1b9fe
review r34: evict oldest synthetic tool media at cap saturation inste…
ThomasK33 Aug 26, 2026
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
7 changes: 3 additions & 4 deletions scripts/rlm-eval/scenarios.ts
Original file line number Diff line number Diff line change
Expand Up @@ -29,7 +29,6 @@ export interface EvalConfig {
id: string;
experiments: {
programmaticToolCalling: boolean;
programmaticToolCallingExclusive?: boolean;
rlm: boolean;
};
/** Optional prompting lever, sent as additionalSystemInstructions. */
Expand Down Expand Up @@ -236,13 +235,13 @@ export const CONFIGS: EvalConfig[] = [
id: "flat-bash",
experiments: { programmaticToolCalling: false, rlm: false },
},
// PTC is exclusive-only (supplement mode measured ~2x flat tokens/cost and
// was removed): this measures the exclusive code_execution toolset without
// the persistent kernel.
{
id: "ptc-only",
experiments: { programmaticToolCalling: true, rlm: false },
},
// RLM is exclusive-only (supplement-mode RLM measured ~2x flat tokens/cost
// and was removed): the rlm flag alone yields the kernel-first exclusive
// toolset. The explicit exclusive flag is redundant but harmless.
{
id: "rlm-excl",
experiments: { programmaticToolCalling: true, rlm: true },
Expand Down
5 changes: 0 additions & 5 deletions src/browser/components/CommandPalette/CommandPalette.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -69,9 +69,6 @@ export const CommandPalette: React.FC<CommandPaletteProps> = ({ getSlashContext
);
const rlmExperimentEnabled = useExperimentValue(EXPERIMENT_IDS.RLM);
const ptcExperimentEnabled = useExperimentValue(EXPERIMENT_IDS.PROGRAMMATIC_TOOL_CALLING);
const ptcExclusiveExperimentEnabled = useExperimentValue(
EXPERIMENT_IDS.PROGRAMMATIC_TOOL_CALLING_EXCLUSIVE
);
const slashContext = getSlashContext?.();
const slashWorkspaceId = slashContext?.workspaceId;

Expand Down Expand Up @@ -306,7 +303,6 @@ export const CommandPalette: React.FC<CommandPaletteProps> = ({ getSlashContext
memoryConsolidation: memoryConsolidationExperimentEnabled,
rlm: rlmExperimentEnabled,
programmaticToolCalling: ptcExperimentEnabled,
programmaticToolCallingExclusive: ptcExclusiveExperimentEnabled,
}),
});
const section = "Slash Commands";
Expand Down Expand Up @@ -385,7 +381,6 @@ export const CommandPalette: React.FC<CommandPaletteProps> = ({ getSlashContext
memoryConsolidationExperimentEnabled,
rlmExperimentEnabled,
ptcExperimentEnabled,
ptcExclusiveExperimentEnabled,
]);

useEffect(() => {
Expand Down
100 changes: 99 additions & 1 deletion src/browser/contexts/ExperimentsContext.test.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,11 @@ import { copyFile, mkdir, mkdtemp, readFile, rm, writeFile } from "node:fs/promi
import { dirname, join } from "node:path";
import { fileURLToPath } from "node:url";
import { GlobalWindow } from "happy-dom";
import { EXPERIMENT_IDS, getExperimentKey } from "@/common/constants/experiments";
import {
EXPERIMENT_IDS,
getExperimentKey,
getLegacyPtcExclusiveExperimentKey,
} from "@/common/constants/experiments";
import { requireTestModule, type RecursivePartial } from "@/browser/testUtils";
import type * as APIModule from "./API";
import type { APIClient } from "./API";
Expand Down Expand Up @@ -248,4 +252,98 @@ describe("ExperimentsProvider", () => {
expect(getByTestId("toggle").textContent).toBe("true");
});
});

test("initialization stamps the legacy mirror over a stale explicit false", async () => {
// An old renderer can leave ptc:true beside a stale legacy exclusive
// `false`; upgrading without touching the toggle previously never rewrote
// the mirror, and a downgraded renderer treats the stale explicit key as
// an override that wins over the backend flag β€” resuming the removed
// supplement posture (r33). Initialization reconciles it.
currentClientMock = {
experiments: {
setOverride: mock(() => Promise.resolve()),
getOverrides: mock(() => Promise.resolve({})),
},
};

globalThis.window.localStorage.setItem(
getExperimentKey(EXPERIMENT_IDS.PROGRAMMATIC_TOOL_CALLING),
JSON.stringify(true)
);
globalThis.window.localStorage.setItem(
getLegacyPtcExclusiveExperimentKey(),
JSON.stringify(false)
);

function Probe() {
const enabled = useExperimentValue(EXPERIMENT_IDS.PROGRAMMATIC_TOOL_CALLING);
return <div data-testid="probe">{String(enabled)}</div>;
}

const { getByTestId } = render(
<APIProvider client={currentClientMock as APIClient}>
<ExperimentsProvider>
<Probe />
</ExperimentsProvider>
</APIProvider>
);

expect(getByTestId("probe").textContent).toBe("true");
await waitFor(() => {
expect(globalThis.window.localStorage.getItem(getLegacyPtcExclusiveExperimentKey())).toBe(
"true"
);
});
});

test("stale legacy exclusive true reads as PTC on, and toggling PTC rewrites the legacy key", async () => {
currentClientMock = {
experiments: {
setOverride: mock(() => Promise.resolve()),
getOverrides: mock(() => Promise.resolve({})),
},
};

// Pre-merge state: "PTC Exclusive Mode" enabled β€” exactly the posture
// merged PTC activates, so the upgrade must keep PTC on.
globalThis.window.localStorage.setItem(
getLegacyPtcExclusiveExperimentKey(),
JSON.stringify(true)
);

function Toggle() {
const [enabled, setEnabled] = useExperiment(EXPERIMENT_IDS.PROGRAMMATIC_TOOL_CALLING);
return (
<button data-testid="toggle" onClick={() => setEnabled(!enabled)}>
{String(enabled)}
</button>
);
}

const { getByTestId } = render(
<APIProvider client={currentClientMock as APIClient}>
<ExperimentsProvider>
<Toggle />
</ExperimentsProvider>
</APIProvider>
);

expect(getByTestId("toggle").textContent).toBe("true");

// Toggling PTC off must rewrite the legacy key too: a downgraded renderer
// treats it as an explicit override that wins over the mirrored backend
// value, so a stale entry would resurrect the pre-merge posture.
fireEvent.click(getByTestId("toggle"));
await waitFor(() => {
expect(getByTestId("toggle").textContent).toBe("false");
});
expect(globalThis.window.localStorage.getItem(getLegacyPtcExclusiveExperimentKey())).toBe(
"false"
);
expect(
globalThis.window.localStorage.getItem(
getExperimentKey(EXPERIMENT_IDS.PROGRAMMATIC_TOOL_CALLING)
)
).toBe("false");
});
});
60 changes: 60 additions & 0 deletions src/browser/contexts/ExperimentsContext.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -8,11 +8,14 @@ import React, {
} from "react";
import {
type ExperimentId,
EXPERIMENT_IDS,
EXPERIMENTS,
getExperimentKey,
getLegacyPtcExclusiveExperimentKey,
isExperimentSupportedOnPlatform,
} from "@/common/constants/experiments";
import { getStorageChangeEvent } from "@/common/constants/events";
import { readPersistedState, updatePersistedState } from "@/browser/hooks/usePersistedState";
import { useAPI } from "@/browser/contexts/API";

/**
Expand Down Expand Up @@ -43,11 +46,30 @@ function isExperimentSupported(experimentId: ExperimentId): boolean {
return isExperimentSupportedOnPlatform(experimentId, getCurrentDesktopPlatform());
}

/**
* Upgrade alias (see LEGACY_PTC_EXCLUSIVE_EXPERIMENT_ID): a stored legacy
* exclusive `true` opted into exactly the posture merged PTC activates, so PTC
* reads as enabled β€” winning even over an explicit supplement-off value,
* matching the backend read alias. setExperimentState rewrites the legacy key
* on every PTC toggle, so the alias never overrides a choice made in this
* build.
*/
export function hasLegacyPtcExclusiveOverride(): boolean {
return readPersistedState<unknown>(getLegacyPtcExclusiveExperimentKey(), undefined) === true;
}

/**
* Get explicit localStorage override for an experiment.
* Returns undefined if no value is set or parsing fails.
*/
function getExperimentOverrideSnapshot(experimentId: ExperimentId): boolean | undefined {
if (
experimentId === EXPERIMENT_IDS.PROGRAMMATIC_TOOL_CALLING &&
hasLegacyPtcExclusiveOverride()
) {
return true;
}

const key = getExperimentKey(experimentId);

try {
Expand Down Expand Up @@ -97,6 +119,18 @@ function setExperimentState(experimentId: ExperimentId, enabled: boolean): void
try {
window.localStorage.setItem(key, JSON.stringify(enabled));

// Downgrade sync (see LEGACY_PTC_EXCLUSIVE_EXPERIMENT_ID): a downgraded
// renderer reads the pre-merge exclusive key as an explicit override that
// wins over the mirrored backend value in its send options, so a stale
// entry would resurrect supplement mode (stale false) or re-enable PTC
// after the user turned it off (stale true). Keep it equal to PTC.
// Routed through updatePersistedState so the mirror participates in the
// shared write-listener/subscriber notification path like other
// persisted preferences.
if (experimentId === EXPERIMENT_IDS.PROGRAMMATIC_TOOL_CALLING) {
updatePersistedState(getLegacyPtcExclusiveExperimentKey(), enabled);
Comment thread
ThomasK33 marked this conversation as resolved.
}

// Dispatch custom event for same-tab synchronization
const customEvent = new CustomEvent(getStorageChangeEvent(key), {
detail: { key, newValue: enabled },
Expand All @@ -107,6 +141,28 @@ function setExperimentState(experimentId: ExperimentId, enabled: boolean): void
}
}

/**
* Upgrade reconciliation for the legacy exclusive mirror (r33): an old
* renderer can leave `programmatic-tool-calling: true` alongside a stale
* legacy exclusive `false` (or none), and setExperimentState rewrites the
* mirror only on toggles β€” a user who upgrades and never touches the setting
* would downgrade into the removed supplement posture, because a downgraded
* renderer treats the stale explicit legacy key as an override that wins over
* the backend's mirrored flag. Keep the mirror stamped whenever the EFFECTIVE
* PTC state (local override first, else the backend override) is enabled.
* Only the enabled state needs stamping: a legacy `true` already aliases
* effective PTC to true, so a disagreeing pair can only be
* (ptc: true, legacy: false/absent).
*/
function reconcileLegacyPtcExclusiveMirror(
backendOverrides: Partial<Record<ExperimentId, boolean>> | null
): void {
const local = getExperimentOverrideSnapshot(EXPERIMENT_IDS.PROGRAMMATIC_TOOL_CALLING);
const effective = local ?? backendOverrides?.[EXPERIMENT_IDS.PROGRAMMATIC_TOOL_CALLING];
if (effective !== true || hasLegacyPtcExclusiveOverride()) return;
updatePersistedState(getLegacyPtcExclusiveExperimentKey(), true);
}

/**
* Context value type - provides setter function.
* Individual experiment values are accessed via useExperimentValue hook.
Expand Down Expand Up @@ -179,10 +235,14 @@ export function ExperimentsProvider(props: { children: React.ReactNode }) {
const overrides = await api.experiments.getOverrides();
if (!cancelled) {
setBackendOverrides(overrides);
reconcileLegacyPtcExclusiveMirror(overrides);
}
} catch {
if (!cancelled) {
setBackendOverrides(null);
// Still reconciles the purely-local stale pair (ptc: true,
// legacy: false/absent) even when the backend is unreachable.
reconcileLegacyPtcExclusiveMirror(null);
}
}
};
Expand Down
6 changes: 0 additions & 6 deletions src/browser/features/ChatInput/index.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -326,9 +326,6 @@ const ChatInputInner: React.FC<ChatInputProps> = (props) => {
);
const rlmExperimentEnabled = useExperimentValue(EXPERIMENT_IDS.RLM);
const ptcExperimentEnabled = useExperimentValue(EXPERIMENT_IDS.PROGRAMMATIC_TOOL_CALLING);
const ptcExclusiveExperimentEnabled = useExperimentValue(
EXPERIMENT_IDS.PROGRAMMATIC_TOOL_CALLING_EXCLUSIVE
);
const atMentionProjectPath =
variant === "creation" && props.kind !== "scratch" ? props.projectPath : null;
const asyncCommandScopeRef = useRef<{ variant: typeof variant; workspaceId: string | null }>({
Expand Down Expand Up @@ -1752,7 +1749,6 @@ const ChatInputInner: React.FC<ChatInputProps> = (props) => {
memoryConsolidation: memoryConsolidationExperimentEnabled,
rlm: rlmExperimentEnabled,
programmaticToolCalling: ptcExperimentEnabled,
programmaticToolCallingExclusive: ptcExclusiveExperimentEnabled,
}),
});
setCommandSuggestions((prev) => replaceSuggestions(prev, suggestions));
Expand All @@ -1769,7 +1765,6 @@ const ChatInputInner: React.FC<ChatInputProps> = (props) => {
memoryConsolidationExperimentEnabled,
rlmExperimentEnabled,
ptcExperimentEnabled,
ptcExclusiveExperimentEnabled,
]);

// Watch input/cursor for `\symbol` backslash commands and surface the menu.
Expand Down Expand Up @@ -1807,7 +1802,6 @@ const ChatInputInner: React.FC<ChatInputProps> = (props) => {
memoryConsolidation: memoryConsolidationExperimentEnabled,
rlm: rlmExperimentEnabled,
programmaticToolCalling: ptcExperimentEnabled,
programmaticToolCallingExclusive: ptcExclusiveExperimentEnabled,
}),
});

Expand Down
13 changes: 0 additions & 13 deletions src/browser/features/Settings/Sections/ExperimentsSection.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -695,9 +695,6 @@ export function ExperimentsSection() {
const workspaceHeartbeatsEnabled = useExperimentValue(EXPERIMENT_IDS.WORKSPACE_HEARTBEATS);
const memoryEnabled = useExperimentValue(EXPERIMENT_IDS.MEMORY);
const ptcEnabled = useExperimentValue(EXPERIMENT_IDS.PROGRAMMATIC_TOOL_CALLING);
const ptcExclusiveEnabled = useExperimentValue(
EXPERIMENT_IDS.PROGRAMMATIC_TOOL_CALLING_EXCLUSIVE
);
const settingsConfigRequestRef = useRef<{
api: APIClient;
request: Promise<SettingsConfig>;
Expand Down Expand Up @@ -808,16 +805,6 @@ export function ExperimentsSection() {
<SubExperimentRows experimentIds={PTC_SUB_EXPERIMENT_IDS} />
</ExperimentSettingsPanel>
)}
{/* RLM rides EITHER accepted PTC parent (toolAssembly accepts
exclusive + rlm too); render under Exclusive only when plain
PTC is off so the row never appears twice. */}
{exp.id === EXPERIMENT_IDS.PROGRAMMATIC_TOOL_CALLING_EXCLUSIVE &&
ptcExclusiveEnabled &&
!ptcEnabled && (
<ExperimentSettingsPanel>
<SubExperimentRows experimentIds={PTC_SUB_EXPERIMENT_IDS} />
</ExperimentSettingsPanel>
)}
{exp.id === EXPERIMENT_IDS.PORTABLE_DESKTOP && <PortableDesktopExperimentWarning />}
{exp.id === EXPERIMENT_IDS.CONFIGURABLE_BIND_URL && <ConfigurableBindUrlControls />}
</React.Fragment>
Expand Down
31 changes: 30 additions & 1 deletion src/browser/hooks/useExperiments.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -8,7 +8,11 @@

import { afterEach, beforeEach, describe, expect, test } from "bun:test";
import { GlobalWindow } from "happy-dom";
import { EXPERIMENT_IDS, getExperimentKey } from "@/common/constants/experiments";
import {
EXPERIMENT_IDS,
getExperimentKey,
getLegacyPtcExclusiveExperimentKey,
} from "@/common/constants/experiments";
import { isExperimentEnabled } from "./useExperiments";

describe("isExperimentEnabled", () => {
Expand Down Expand Up @@ -60,4 +64,29 @@ describe("isExperimentEnabled", () => {
globalThis.window.localStorage.setItem(key, JSON.stringify("test"));
expect(isExperimentEnabled(EXPERIMENT_IDS.PROGRAMMATIC_TOOL_CALLING)).toBeUndefined();
});

test("legacy exclusive true reads as PTC enabled, winning over an explicit PTC false", () => {
// Pre-merge builds stored "PTC Exclusive Mode" under its own key; that
// posture is exactly what merged PTC activates, so it must keep PTC on
// even when the old supplement flag was explicitly off.
globalThis.window.localStorage.setItem(
getLegacyPtcExclusiveExperimentKey(),
JSON.stringify(true)
);
expect(isExperimentEnabled(EXPERIMENT_IDS.PROGRAMMATIC_TOOL_CALLING)).toBe(true);

globalThis.window.localStorage.setItem(
getExperimentKey(EXPERIMENT_IDS.PROGRAMMATIC_TOOL_CALLING),
JSON.stringify(false)
);
expect(isExperimentEnabled(EXPERIMENT_IDS.PROGRAMMATIC_TOOL_CALLING)).toBe(true);
});

test("legacy exclusive false does not alias onto PTC", () => {
globalThis.window.localStorage.setItem(
getLegacyPtcExclusiveExperimentKey(),
JSON.stringify(false)
);
expect(isExperimentEnabled(EXPERIMENT_IDS.PROGRAMMATIC_TOOL_CALLING)).toBeUndefined();
});
});
Loading
Loading