Preserve prior divergence when a task is re-locked after plan changes. - Persist immutable spec locks, current-plan evidence, and drift reports. - Reconcile retained divergence into re-approved alignment state across engine, API, and dashboard views. - Fence Plan Review acceptance and schema upgrades while retaining migration identity parity. Files changed: .changeset/fn-8845-spec-lock-drift-report.md | 7 + .changeset/fn-8943-spec-lock-divergence.md | 7 + docs/architecture.md | 17 +- docs/dashboard-guide.md | 3 + docs/missions.md | 4 + .../core/src/__tests__/planner/spec-lock.test.ts | 158 ++++++++++++++ .../src/__tests__/postgres/schema-applier.test.ts | 30 ++- .../postgres/task-dependency-mutation.pg.test.ts | 74 +++++++ packages/core/src/index.gate.ts | 4 + packages/core/src/index.ts | 4 + packages/core/src/planner/drift-report.ts | 152 +++++++++++++ packages/core/src/planner/spec-lock.ts | 182 ++++++++++++++++ .../migrations/0050_spec_lock_drift_report.sql | 29 +++ .../0051_spec_lock_source_revision_bigint.sql | 3 + packages/core/src/postgres/schema-applier.ts | 25 ++- packages/core/src/postgres/schema/project.ts | 13 ++ packages/core/src/store.ts | 242 ++++++++++++++++++++- .../core/src/task-store/branch-and-pr-entities.ts | 32 ++- packages/core/src/task-store/project-store-ops.ts | 34 ++- packages/core/src/task-store/task-update.ts | 66 +++++- packages/core/src/task-store/update-task-deps.ts | 31 ++- packages/dashboard/app/api.ts | 3 + packages/dashboard/app/api/tasks/tasks.ts | 22 ++ .../dashboard/app/components/MissionManager.css | 21 ++ .../dashboard/app/components/MissionManager.tsx | 41 +++- .../dashboard/app/components/TaskDetailModal.css | 26 +++ .../dashboard/app/components/TaskDetailModal.tsx | 62 +++++- .../__tests__/TaskDetailModal.spec-lock.test.tsx | 80 +++++++ .../__tests__/TaskDetailModal.test-helpers.ts | 2 + .../src/__tests__/plan-approval-status.pg.test.ts | 81 ++++++- .../src/routes/register-task-workflow-routes.ts | 59 ++++- .../src/__tests__/mission-feature-sync.test.ts | 15 +- .../src/__tests__/spec-drift-reconciler.test.ts | 108 +++++++++ .../engine/src/executor/execute-workflow-graph.ts | 49 ++++- .../engine/src/missions/mission-feature-sync.ts | 55 ++++- packages/engine/src/project-engine.ts | 33 +++ packages/engine/src/spec-drift-reconciler.ts | 105 +++++++++ packages/engine/src/triage.ts | 29 +++ 38 files changed, 1842 insertions(+), 66 deletions(-) Fusion-Task-Id: FN-8943 Fusion-Task-Lineage: 2a7f8a38-99aa-4c4b-8c36-41d14c466d21 Co-authored-by: Fusion (runfusion.ai) <noreply@runfusion.ai>
75 lines
2.3 KiB
TypeScript
75 lines
2.3 KiB
TypeScript
/**
|
|
* Compatibility barrel for the dashboard client API surface.
|
|
*
|
|
* Existing callers import from `../api` / `../../api`; keep this entrypoint stable
|
|
* while implementation lives under `app/api/*` modules.
|
|
*/
|
|
import { api } from "./api/client/client";
|
|
|
|
export * from "./api/legacy";
|
|
export * from "./api/settings/provider-status";
|
|
export * from "./api/planning/models-usage";
|
|
export * from "./api/chat";
|
|
export * from "./api-node";
|
|
export * from "./api/system/report";
|
|
|
|
/*
|
|
FNXC:AgentActivityStream 2026-08-09-09:38:
|
|
The monitoring clients receive durable event frames and an explicit truncation marker on the same SSE event name. A marker is not an activity row; consumers fetch the omitted range through the typed history route.
|
|
*/
|
|
export type AgentActivityEventType =
|
|
| "task:started"
|
|
| "task:handed-off"
|
|
| "task:completed"
|
|
| "agent:state-changed"
|
|
| "workflow:gate-passed"
|
|
| "workflow:gate-failed"
|
|
| "approval:requested";
|
|
|
|
export interface AgentActivityEvent {
|
|
seq: string;
|
|
eventId: string;
|
|
projectId: string;
|
|
agentId: string;
|
|
agentAttribution: "agent" | "lane" | "actor";
|
|
taskId: string | null;
|
|
type: AgentActivityEventType;
|
|
fromAgentId: string | null;
|
|
toAgentId: string | null;
|
|
summary: string;
|
|
occurredAt: string;
|
|
metadata: Record<string, unknown> | null;
|
|
}
|
|
|
|
export interface AgentActivityTruncatedFrame {
|
|
truncated: true;
|
|
fromSeq: string;
|
|
toSeq: string;
|
|
}
|
|
|
|
export type AgentActivitySseFrame = AgentActivityEvent | AgentActivityTruncatedFrame;
|
|
|
|
export interface AgentActivityPage {
|
|
events: AgentActivityEvent[];
|
|
nextCursor: string | null;
|
|
}
|
|
|
|
export async function getAgentActivity(
|
|
params: Record<string, string | number | undefined> = {},
|
|
options?: Pick<RequestInit, "signal">,
|
|
): Promise<AgentActivityPage> {
|
|
const query = new URLSearchParams(
|
|
Object.entries(params)
|
|
.filter((entry): entry is [string, string | number] => entry[1] !== undefined)
|
|
.map(([key, value]) => [key, String(value)]),
|
|
);
|
|
/*
|
|
FNXC:AgentActivityClient 2026-08-09-23:44:
|
|
Activity seeding must use the shared client so project auth, base-path handling, and request identity match every dashboard API request.
|
|
*/
|
|
return api<AgentActivityPage>(`/agent-activity${query.size ? `?${query}` : ""}`, options);
|
|
}
|
|
|
|
export { fetchSpecLock } from "./api/tasks/tasks";
|
|
export type { SpecLockResponse } from "./api/tasks/tasks";
|