Move to threads based system.

This commit is contained in:
Gabriel Brown
2026-06-22 10:37:26 -04:00
parent 8ae6c4b533
commit 206b64176b
82 changed files with 6169 additions and 1930 deletions
+108
View File
@@ -50,6 +50,7 @@ const refreshOwnedSpoon = async (
ownerId: Id<'users'>,
spoonId: Id<'spoons'>,
kind: 'manual_check' | 'scheduled_check' = 'manual_check',
allowAutoSync = true,
): Promise<{
success: boolean;
status: ReturnType<typeof toStatus>;
@@ -200,6 +201,87 @@ const refreshOwnedSpoon = async (
status: status === 'diverged' ? 'needs_review' : 'clean',
summary: `GitHub refresh complete: ${upstreamCompare.aheadBy} upstream commit(s), ${forkCompare.aheadBy} fork-only commit(s).`,
});
if (status === 'behind' && forkCompare.aheadBy === 0 && allowAutoSync) {
try {
await syncForkBranch(octokit, {
forkOwner,
forkRepo,
branch: resolvedForkBranch,
});
await ctx.runMutation(internal.syncRuns.patchInternal, {
syncRunId,
status: 'merged',
decision: 'auto_synced',
summary:
'Fork had no custom commits, so Spoon synced it with upstream automatically.',
});
return await refreshOwnedSpoon(ctx, ownerId, spoonId, kind, false);
} catch (syncError) {
const message =
syncError instanceof Error ? syncError.message : String(syncError);
const threadId = await ctx.runMutation(
internal.threads.createMaintenanceThread,
{
spoonId,
ownerId,
source: 'merge_conflict',
title: `Resolve upstream sync conflict for ${spoon.name}`,
summary: `GitHub refused the automatic upstream sync: ${message}`,
upstreamFrom: upstreamCompare.mergeBaseSha,
upstreamTo: upstreamCompare.headSha ?? `${Date.now()}`,
forkHeadAtCreation: forkCompare.headSha,
mergeBaseAtCreation:
upstreamCompare.mergeBaseSha ?? forkCompare.mergeBaseSha,
relatedSyncRunId: syncRunId,
jobType: 'conflict_resolution',
},
);
await ctx.runMutation(internal.syncRuns.patchInternal, {
syncRunId,
threadId,
status: 'conflict',
decision: 'thread_created',
error: message,
});
await ctx.runMutation(internal.spoons.patchSyncFields, {
spoonId,
syncStatus: 'conflict',
lastError: message,
});
return {
success: true,
status: 'unknown' as const,
upstreamAheadBy: upstreamCompare.aheadBy,
forkAheadBy: forkCompare.aheadBy,
};
}
}
if (status === 'diverged') {
const threadId = await ctx.runMutation(
internal.threads.createMaintenanceThread,
{
spoonId,
ownerId,
source: 'upstream_update',
title: `Review upstream changes for ${spoon.name}`,
summary: `Upstream has ${upstreamCompare.aheadBy} commit(s) and the fork has ${forkCompare.aheadBy} custom commit(s). Review whether upstream should be merged, ignored, or resolved in a draft PR.`,
upstreamFrom: upstreamCompare.mergeBaseSha,
upstreamTo: upstreamCompare.headSha ?? `${Date.now()}`,
forkHeadAtCreation: forkCompare.headSha,
mergeBaseAtCreation:
upstreamCompare.mergeBaseSha ?? forkCompare.mergeBaseSha,
relatedSyncRunId: syncRunId,
jobType: 'maintenance_review',
},
);
await ctx.runMutation(internal.syncRuns.patchInternal, {
syncRunId,
threadId,
decision: 'thread_created',
});
}
return {
success: true,
status,
@@ -301,6 +383,32 @@ export const syncForkWithUpstream = action({
} catch (error) {
const message = error instanceof Error ? error.message : String(error);
const conflict = message.toLowerCase().includes('conflict');
if (conflict) {
const threadId = await ctx.runMutation(
internal.threads.createMaintenanceThread,
{
spoonId,
ownerId,
source: 'merge_conflict',
title: `Resolve upstream sync conflict for ${spoon.name}`,
summary: `GitHub reported a conflict while syncing upstream into this fork: ${message}`,
upstreamTo:
state.upstreamHeadSha ??
spoon.lastUpstreamCommit ??
`${Date.now()}`,
forkHeadAtCreation: state.forkHeadSha ?? spoon.lastForkCommit,
mergeBaseAtCreation:
state.mergeBaseSha ?? spoon.lastMergeBaseCommit,
relatedSyncRunId: syncRunId,
jobType: 'conflict_resolution',
},
);
await ctx.runMutation(internal.syncRuns.patchInternal, {
syncRunId,
threadId,
decision: 'thread_created',
});
}
await ctx.runMutation(internal.syncRuns.patchInternal, {
syncRunId,
status: conflict ? 'conflict' : 'failed',