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
2 changes: 2 additions & 0 deletions src/main/channels/project-create.ts
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,7 @@ import {
readStore,
writeStore,
} from './utils/project-store';
import { getTaskManager } from './utils/task-manager';
import { IpcChannels } from '.';
import type { Project, ProjectCreateResult } from '../../types';

Expand Down Expand Up @@ -49,6 +50,7 @@ export const projectCreate = defineChannel( {
};
store.projects.push( project );
writeStore( store );
getTaskManager().hydrateProject( project );
return { status: 'ok', project };
},
} );
94 changes: 52 additions & 42 deletions src/main/channels/utils/task-manager.ts
Original file line number Diff line number Diff line change
Expand Up @@ -397,52 +397,62 @@ class TaskManager {
}
}

private hydrate(): void {
for ( const project of readProjectStore().projects ) {
// projectId repair: re-linking a folder mints a new project id,
// but its tasks.json / task-runs.json keep the old one — which
// orphans every task under a project that no longer exists. The
// folder's current project id is authoritative.
const defs = readTasks( project.path );
let defsChanged = false;
for ( const def of defs ) {
if ( def.projectId !== project.id ) {
def.projectId = project.id;
defsChanged = true;
}
}
if ( defsChanged ) {
writeTasks( project.path, defs );
hydrateProject( project: { id: string; path: string } ): void {
// projectId repair: re-linking a folder mints a new project id,
// but its tasks.json / task-runs.json keep the old one — which
// orphans every task under a project that no longer exists. The
// folder's current project id is authoritative.
const defs = readTasks( project.path );
let defsChanged = false;
for ( const def of defs ) {
if ( def.projectId !== project.id ) {
def.projectId = project.id;
defsChanged = true;
}
this.definitions.set( project.id, defs );

// Stale-run repair: a persisted run still in a non-terminal state
// means the app died mid-run. Same projectId repair as above.
const runs = readRuns( project.path );
let runsChanged = false;
for ( const run of runs ) {
if ( run.projectId !== project.id ) {
run.projectId = project.id;
runsChanged = true;
}
if (
run.status === 'queued' ||
run.status === 'running' ||
run.status === 'needs-permission'
) {
run.status = 'error';
run.error =
'Interrupted — the app was closed during this run.';
run.summary = run.error;
run.endedAt = run.endedAt ?? Date.now();
run.pendingPermissionCount = 0;
runsChanged = true;
}
}
if ( defsChanged ) {
writeTasks( project.path, defs );
}
this.definitions.set( project.id, defs );

// Stale-run repair: a persisted run still in a non-terminal state
// means the app died mid-run. Same projectId repair as above.
const runs = readRuns( project.path );
let runsChanged = false;
for ( const run of runs ) {
if ( run.projectId !== project.id ) {
run.projectId = project.id;
runsChanged = true;
}
if ( runsChanged ) {
writeRuns( project.path, runs );
if (
run.status === 'queued' ||
run.status === 'running' ||
run.status === 'needs-permission'
) {
run.status = 'error';
run.error = 'Interrupted — the app was closed during this run.';
run.summary = run.error;
run.endedAt = run.endedAt ?? Date.now();
run.pendingPermissionCount = 0;
runsChanged = true;
}
}
if ( runsChanged ) {
writeRuns( project.path, runs );
}

if ( defs.length > 0 ) {
this.emitEvent( {
kind: 'definitions-changed',
projectId: project.id,
} );
}
}

private hydrate(): void {
for ( const project of readProjectStore().projects ) {
this.hydrateProject( project );
}
}

private checkSchedule(): void {
Expand Down
Loading