fix: atomic writer race condition and bulk replace reset to defaults

1. AtomicWriter Race Condition Fix (libs/utils/src/atomic-writer.ts):
   - Changed temp file naming from Date.now() to Date.now() + random hex
   - Uses crypto.randomBytes(4).toString('hex') for uniqueness
   - Prevents ENOENT errors when multiple concurrent writes happen
     within the same millisecond

2. Bulk Replace "Anthropic Direct" Reset (both dialogs):
   - When selecting "Anthropic Direct", now uses DEFAULT_PHASE_MODELS
   - Properly resets thinking levels and other settings to defaults
   - Added thinkingLevel to the change detection comparison
   - Affects both global and project-level bulk replace dialogs
This commit is contained in:
Stefan de Vogelaere
2026-01-20 19:55:13 +01:00
parent 8ffe69feb1
commit 86e3892c66
4 changed files with 23 additions and 23 deletions

View File

@@ -121,11 +121,12 @@ export function ProjectBulkReplaceDialog({
// Find the model from provider that maps to a specific Claude model
const findModelForClaudeAlias = (
provider: ClaudeCompatibleProvider | null,
claudeAlias: ClaudeModelAlias
claudeAlias: ClaudeModelAlias,
phase: PhaseModelKey
): PhaseModelEntry => {
if (!provider) {
// Anthropic Direct - use Claude model directly
return { model: claudeAlias };
// Anthropic Direct - reset to default phase model (includes correct thinking levels)
return DEFAULT_PHASE_MODELS[phase];
}
// Find model that maps to this Claude alias
@@ -152,7 +153,7 @@ export function ProjectBulkReplaceDialog({
const globalEntry = phaseModels[phase] ?? DEFAULT_PHASE_MODELS[phase];
const currentEntry = projectOverrides[phase] || globalEntry;
const claudeAlias = getClaudeModelAlias(currentEntry);
const newEntry = findModelForClaudeAlias(selectedProviderConfig, claudeAlias);
const newEntry = findModelForClaudeAlias(selectedProviderConfig, claudeAlias, phase);
// Get display names
const getCurrentDisplay = (): string => {
@@ -175,7 +176,9 @@ export function ProjectBulkReplaceDialog({
};
const isChanged =
currentEntry.model !== newEntry.model || currentEntry.providerId !== newEntry.providerId;
currentEntry.model !== newEntry.model ||
currentEntry.providerId !== newEntry.providerId ||
currentEntry.thinkingLevel !== newEntry.thinkingLevel;
return {
phase,

View File

@@ -112,11 +112,12 @@ export function BulkReplaceDialog({ open, onOpenChange }: BulkReplaceDialogProps
// Find the model from provider that maps to a specific Claude model
const findModelForClaudeAlias = (
provider: ClaudeCompatibleProvider | null,
claudeAlias: ClaudeModelAlias
claudeAlias: ClaudeModelAlias,
phase: PhaseModelKey
): PhaseModelEntry => {
if (!provider) {
// Anthropic Direct - use Claude model directly
return { model: claudeAlias };
// Anthropic Direct - reset to default phase model (includes correct thinking levels)
return DEFAULT_PHASE_MODELS[phase];
}
// Find model that maps to this Claude alias
@@ -141,7 +142,7 @@ export function BulkReplaceDialog({ open, onOpenChange }: BulkReplaceDialogProps
return ALL_PHASES.map((phase) => {
const currentEntry = phaseModels[phase] ?? DEFAULT_PHASE_MODELS[phase];
const claudeAlias = getClaudeModelAlias(currentEntry);
const newEntry = findModelForClaudeAlias(selectedProviderConfig, claudeAlias);
const newEntry = findModelForClaudeAlias(selectedProviderConfig, claudeAlias, phase);
// Get display names
const getCurrentDisplay = (): string => {
@@ -164,7 +165,9 @@ export function BulkReplaceDialog({ open, onOpenChange }: BulkReplaceDialogProps
};
const isChanged =
currentEntry.model !== newEntry.model || currentEntry.providerId !== newEntry.providerId;
currentEntry.model !== newEntry.model ||
currentEntry.providerId !== newEntry.providerId ||
currentEntry.thinkingLevel !== newEntry.thinkingLevel;
return {
phase,