fix: Reconcile orphaned changesets when phases are manually deleted
Manually deleting phases left their parent changeset as "applied", causing the Plan tab to show a stale "Created N phases" banner with no phases visible. - deletePhase now checks if all phases from a changeset are gone and marks it reverted - PlanSection filters out dismissed agents so dismissed banners stay hidden - revertChangeSet marks reverted before entity deletion to prevent ghost state on partial failure - deletePhase invalidation now includes listChangeSets
This commit is contained in:
@@ -33,4 +33,10 @@ export interface ChangeSetRepository {
|
||||
findByInitiativeId(initiativeId: string): Promise<ChangeSet[]>;
|
||||
findByAgentId(agentId: string): Promise<ChangeSet[]>;
|
||||
markReverted(id: string): Promise<ChangeSet>;
|
||||
|
||||
/**
|
||||
* Find applied changesets that have a 'create' entry for the given entity.
|
||||
* Used to reconcile changeset status when entities are manually deleted.
|
||||
*/
|
||||
findAppliedByCreatedEntity(entityType: string, entityId: string): Promise<ChangeSetWithEntries[]>;
|
||||
}
|
||||
|
||||
@@ -4,7 +4,7 @@
|
||||
* Implements ChangeSetRepository interface using Drizzle ORM.
|
||||
*/
|
||||
|
||||
import { eq, desc, asc } from 'drizzle-orm';
|
||||
import { eq, desc, asc, and } from 'drizzle-orm';
|
||||
import { nanoid } from 'nanoid';
|
||||
import type { DrizzleDatabase } from '../../index.js';
|
||||
import { changeSets, changeSetEntries, type ChangeSet } from '../../schema.js';
|
||||
@@ -94,6 +94,32 @@ export class DrizzleChangeSetRepository implements ChangeSetRepository {
|
||||
.orderBy(desc(changeSets.createdAt));
|
||||
}
|
||||
|
||||
async findAppliedByCreatedEntity(entityType: string, entityId: string): Promise<ChangeSetWithEntries[]> {
|
||||
// Find changeset entries matching the entity
|
||||
const matchingEntries = await this.db
|
||||
.select({ changeSetId: changeSetEntries.changeSetId })
|
||||
.from(changeSetEntries)
|
||||
.where(
|
||||
and(
|
||||
eq(changeSetEntries.entityType, entityType as any),
|
||||
eq(changeSetEntries.entityId, entityId),
|
||||
eq(changeSetEntries.action, 'create'),
|
||||
),
|
||||
);
|
||||
|
||||
const results: ChangeSetWithEntries[] = [];
|
||||
const seen = new Set<string>();
|
||||
for (const { changeSetId } of matchingEntries) {
|
||||
if (seen.has(changeSetId)) continue;
|
||||
seen.add(changeSetId);
|
||||
const cs = await this.findByIdWithEntries(changeSetId);
|
||||
if (cs && cs.status === 'applied') {
|
||||
results.push(cs);
|
||||
}
|
||||
}
|
||||
return results;
|
||||
}
|
||||
|
||||
async markReverted(id: string): Promise<ChangeSet> {
|
||||
const [updated] = await this.db
|
||||
.update(changeSets)
|
||||
|
||||
Reference in New Issue
Block a user