diff --git a/packages/core/src/__tests__/same-agent-duplicate-intake.test.ts b/packages/core/src/__tests__/same-agent-duplicate-intake.test.ts index 28ecb2d85b..970b295d1c 100644 --- a/packages/core/src/__tests__/same-agent-duplicate-intake.test.ts +++ b/packages/core/src/__tests__/same-agent-duplicate-intake.test.ts @@ -15,7 +15,7 @@ vi.mock("../task-store/async-persistence.js", async (importOriginal) => ({ })); import { TombstonedTaskResurrectionError } from "../task-store/errors.js"; -import { _maybeAutoArchiveSameAgentDuplicateBackendImpl } from "../task-store/remaining-ops-2.js"; +import { _maybeAutoArchiveSameAgentDuplicateBackendImpl } from "../task-store/task-mutation-ops.js"; import { resolveSameAgentDuplicateIntake } from "../task-store/task-creation.js"; const NOW = new Date().toISOString(); diff --git a/packages/core/src/store.ts b/packages/core/src/store.ts index a0b4f4af32..dc8c1ca5ff 100644 --- a/packages/core/src/store.ts +++ b/packages/core/src/store.ts @@ -97,7 +97,7 @@ import { type TaskIdIntegrityReport } from "./task-id-integrity.js"; import { TASK_JSONB_COLUMNS, type TaskRow, type TaskPersistSerializationContext, type TaskColumnDescriptor } from "./task-store/persistence.js"; import { pgRowToTaskRow as pgRowToTaskRowExternal, rowToTask as rowToTaskExternal, rowToBranchGroup as rowToBranchGroupExternal, generateBranchGroupId as generateBranchGroupIdExternal, computeTimedExecutionMs as computeTimedExecutionMsExternal, archiveEntryToTask as archiveEntryToTaskExternal, summarizeAgentLog as summarizeAgentLogExternal, rowToTaskDocument as rowToTaskDocumentExternal, rowToArtifact as rowToArtifactExternal, rowToTaskDocumentRevision as rowToTaskDocumentRevisionExternal, rowToGoalCitation as rowToGoalCitationExternal } from "./task-store/serialization.js"; import { moveTaskImpl, moveTaskIfImpl, handoffToReviewImpl, moveTaskInternalImpl, type MoveTaskIfResult } from "./task-store/moves.js"; -import { recordGoalCitationsImpl, insertTaskWithFtsRecoveryImpl2, assertTaskIdAvailableImpl, atomicWriteTaskJsonImpl2, createTaskWithDistributedReservationImpl, toStoredWorkflowStepImpl, ensureWorkflowStepForTemplateImpl, resolveEnabledWorkflowStepsImpl, setTaskBranchGroupImpl, getTaskColumnsImpl, prepareWorkflowMovePolicyPreflightImpl, updateTaskCustomFieldsImpl, listWorkflowPromptOverridesForProjectImpl, listWorkflowWorkItemsForTaskImpl, listDueWorkflowWorkItemsImpl, rewriteBlockedByResidueDependentsForRemovalImpl, getAllDocumentsImpl, deleteWorkflowStepImpl, toWorkflowDefinitionImpl, materializeDefaultWorkflowStepsImpl, reconcileTaskCustomFieldsForSchemaImpl, getTaskMovedCountsByDayImpl, getGoalStoreImpl, upsertTaskCommitAssociationImpl } from "./task-store/remaining-ops-4.js"; +import { recordGoalCitationsImpl, insertTaskWithFtsRecoveryImpl2, assertTaskIdAvailableImpl, atomicWriteTaskJsonImpl2, createTaskWithDistributedReservationImpl, toStoredWorkflowStepImpl, ensureWorkflowStepForTemplateImpl, resolveEnabledWorkflowStepsImpl, setTaskBranchGroupImpl, getTaskColumnsImpl, prepareWorkflowMovePolicyPreflightImpl, updateTaskCustomFieldsImpl, listWorkflowPromptOverridesForProjectImpl, listWorkflowWorkItemsForTaskImpl, listDueWorkflowWorkItemsImpl, rewriteBlockedByResidueDependentsForRemovalImpl, getAllDocumentsImpl, deleteWorkflowStepImpl, toWorkflowDefinitionImpl, materializeDefaultWorkflowStepsImpl, reconcileTaskCustomFieldsForSchemaImpl, getTaskMovedCountsByDayImpl, getGoalStoreImpl, upsertTaskCommitAssociationImpl } from "./task-store/workflow-task-create-ops.js"; import { applyLegacyWorkflowStepOverridesImpl, archiveDbImpl, assertNoDependencyCycleImpl, atomicCreateTaskJsonImpl, buildActiveTaskDependencyLookupImpl, buildArchivedAgentLogFieldsImpl, buildTaskIdIntegrityFallbackReportImpl, createBranchGroupImpl, dbImpl, detectAndCacheTaskIdIntegrityReportImpl, findLiveDependentsImpl, findLiveLineageChildrenImpl, getLegacyWorkflowStepSnapshotImpl, getMalformedTaskMetadataReasonImpl, getMergeQueuedTaskIdsAsyncImpl, insertRunAuditEventRowImpl, insertTaskImpl, invokeTaskCreatedHookImpl, isTaskArchivedImpl, isTaskIdPresentInArchivedTasksTableImpl, logTaskCreateConflictImpl, maybeResolveTombstonedTaskIdImpl, mergeTaskIdIntegrityReportsImpl, optionalGroupIdSetImpl, patchTaskRowInTransactionImpl, readConfigFastImpl, readConfigImpl, readPromptForArchiveImpl, readTaskFromDbImpl, reconcileDistributedTaskIdStateOnOpenImpl, recordActivityFromListenerImpl, recordDependencyCycleRejectedAuditImpl, refreshTaskIdIntegrityReportImpl, resolveLocalNodeIdForTaskAllocationImpl, runTaskFtsWriteWithRecoveryImpl, scanAndRecordCitationsImpl, taskIdExistsAnywhereImpl, throwSoftDeletedWriteBlockedImpl, toBuiltInWorkflowStepImpl, trackDeferredTaskCreatedWorkImpl, upsertTaskImpl, withConfigLockImpl, withTaskLockImpl, withWorktreeAllocationLockImpl } from "./task-store/task-id-integrity.js"; import { claimNextToolFailureRetryImpl, createTaskVerificationRequestImpl, claimTaskVerificationRequestImpl, finishTaskVerificationRequestImpl, clearNearDuplicateReferencesToFailSoftImpl, clearWorkflowRunStepInstancesAsyncImpl, clearWorkflowRunStepInstancesImpl, computeMovedSettingsTargetWorkflowIdsImpl, ensureBranchGroupForSourceImpl, ensurePrEntityForSourceImpl, findRecentTasksByContentFingerprintImpl, getActiveMergingTaskImpl, getActivePrEntityBySourceImpl, getBranchGroupByBranchNameImpl, getBranchGroupBySourceImpl, getBranchGroupImpl, getBranchProgressByTaskImpl, getMutationsForRunImpl, getPrEntityByNumberImpl, getPrEntityImpl, getPrThreadStateImpl, getTasksByAssignedAgentImpl, getWorkflowPromptOverridesAsyncImpl, getWorkflowSettingValuesAsyncImpl, getWorkflowSettingValuesImpl, getWorkflowSettingsProjectIdImpl, getWorkflowWorkItemImpl, insertCompletionHandoffWorkflowWorkAuditImpl, listActivePrEntitiesImpl, listBranchGroupsImpl, listPrThreadStatesImpl, listTasksByBranchGroupImpl, listWorkflowSettingValuesForProjectImpl, loadWorkflowRunBranchesImpl, loadWorkflowRunStepInstancesAsyncImpl, loadWorkflowRunStepInstancesImpl, markToolFailureRetryExhaustedAuditImpl, mergeCustomFieldPatchImpl, normalizeMergeRequestStateImpl, normalizeWorkflowWorkItemKindImpl, normalizeWorkflowWorkItemStateImpl, parseWorkflowPromptOverrideJsonImpl, recordPrThreadOutcomeImpl, resetAllStepsToPendingImpl, resetPromptCheckboxesImpl, resolveWorkflowMoveActorImpl, resolveWorkflowSettingDeclarationsImpl, saveWorkflowRunStepInstanceAsyncImpl, saveWorkflowRunStepInstanceImpl, transitionMergeRequestStateImpl, transitionWorkflowWorkItemSyncImpl, updateTaskImpl, updateWorkflowPromptOverridesImpl, upsertMergeRequestRecordImpl, workflowStateForMergeRequestStateImpl } from "./task-store/branch-and-pr-entities.js"; import { addPrInfoImpl, addSteeringCommentImpl, archiveAllDoneImpl, cleanupStaleMergeQueueRowsImpl, clearCompletionHandoffAcceptedMarkerImpl, clearDoneTransientFieldsImpl, clearStaleExecutionStartBranchReferencesImpl, computeWorkflowColumnsGraduationReportImpl, deleteTaskCommentImpl, deleteTaskDocumentImpl, emitUsageEventImpl, enqueueMergeQueueImpl, getAgentLogCountImpl, getAgentLogsImpl, getArtifactImpl, getArtifactsImpl, getAttachmentImpl, getCompletionHandoffAcceptedMarkerImpl, getTaskDocumentImpl, getTaskDocumentRevisionsImpl, getTaskDocumentsImpl, insertArtifactRowImpl, linkGithubIssueImpl, listWorkflowWorkItemsForTaskSyncImpl, moveToDoneImpl, parseDependenciesFromPromptImpl, parseFileScopeFromPromptImpl, parseStepsFromPromptImpl, peekMergeQueueHeadImpl, peekMergeQueueImpl, readPreArchiveColumnFromTaskFileImpl, recordPluginActivationImpl, recordRunAuditEventBackendImpl, removePrInfoByNumberImpl, resolvePrimaryPrInfoImpl, resolveUnarchiveTargetColumnImpl, rewriteLineageChildrenForRemovalImpl, runGitCommandImpl, stopWatchingImpl, syncAgentTaskLinkOnReassignmentImpl, updateArtifactImpl, updateGithubTrackingImpl, updatePrInfoByNumberImpl, updateTaskCommentImpl, upsertPrInfoByNumberImpl, writeArtifactDataImpl } from "./task-store/task-artifacts-ops.js"; @@ -106,7 +106,7 @@ import { getTaskCommitAssociationsByLineageIdImpl, replaceLegacyTaskCommitAssoci import { findRecentTasksBySourceParentTaskIdImpl } from "./task-store/branch-and-pr-entities.js"; import { addTaskCommentImpl, applyBuiltInPromptOverridesSyncImpl, areAllDependenciesDoneImpl, artifactStoredNameImpl, assertWorkflowIrTraitsValidImpl, clearActivityLogImpl, clearTaskWorkflowSelectionImpl, deleteTaskByIdImpl, getDefaultWorkflowIdImpl, getInsightStoreImpl, getMergeQueuedTaskIdsImpl, getMergeRequestRecordImpl, getMergeRequestRecordAsyncImpl, getResearchStoreImpl, getTaskIdFromDirImpl, getTodoStoreImpl, getWorkflowWorkItemByIdentityImpl, hasActiveTaskImpl, invalidateConfigCacheAfterMigrationImpl, isTaskIdConflictErrorImpl, listLegacyAutoMergeStampCandidatesImpl, readTaskRowFromDbImpl, recordBranchGroupMemberLandedImpl, refreshDatabaseHealthImpl, resolveEffectiveWorkflowIdSyncImpl, resolveTaskCustomFieldDefsSyncImpl, resolveWorkflowBypassGuardsImpl, serializeConfigForDiskImpl, setPluginWorkflowStepTemplatesImpl, shouldSkipWorkflowMovePoliciesImpl, suppressWatcherImpl, upsertTaskWithFtsRecoveryImpl } from "./task-store/task-store-helpers.js"; import { getTaskSelectClauseImpl2, createTaskPersistSerializationContextImpl, getTaskPersistValuesImpl, getTaskPatchDescriptorsImpl, normalizeTaskFromDiskImpl, writeTaskJsonFileImpl, rowToPrEntityImpl, generatePrEntityIdImpl, readTaskForMoveImpl, rowToMergeQueueEntryImpl, rowToMergeRequestRecordImpl, rowToCompletionHandoffMarkerImpl, rowToWorkflowWorkItemImpl, rowToRunAuditEventImpl } from "./task-store/task-row-mappers.js"; -import { getTaskSelectClauseWithActivityLogLimitImpl, getChangedTaskColumnsImpl, getSoftDeletedWriteConflictImpl, readTaskJsonImpl, writeConfigImpl, _maybeAutoArchiveSameAgentDuplicateBackendImpl, updateBranchGroupImpl, updatePrEntityImpl, listTasksForGithubTrackingReconcileImpl, listTasksForGitlabTrackingReconcileImpl, renewCheckoutLeaseImpl, updateTaskAtomicImpl, getWorkflowPromptOverridesImpl, updateWorkflowSettingValuesImpl, rollbackConfigurationImpl, cancelActiveWorkflowWorkItemsForTaskImpl, setCompletionHandoffAcceptedMarkerImpl, reconcileLegacyAutoMergeStampsImpl, recoverExpiredMergeQueueLeasesImpl, rewriteDependentsForRemovalImpl, cleanupBranchForTaskImpl, addAttachmentImpl, deleteAttachmentImpl, registerArtifactImpl, updatePrInfoImpl, unlinkGithubIssueImpl, cleanupArchivedTasksImpl, generatePromptFromArchiveEntryImpl, listWorkflowOccupantTaskIdsImpl, evacuateCustomColumnsToLegacyImpl, listApprovedCliAutonomyAdaptersImpl, closeImpl, getActivityLogImpl } from "./task-store/remaining-ops-2.js"; +import { getTaskSelectClauseWithActivityLogLimitImpl, getChangedTaskColumnsImpl, getSoftDeletedWriteConflictImpl, readTaskJsonImpl, writeConfigImpl, _maybeAutoArchiveSameAgentDuplicateBackendImpl, updateBranchGroupImpl, updatePrEntityImpl, listTasksForGithubTrackingReconcileImpl, listTasksForGitlabTrackingReconcileImpl, renewCheckoutLeaseImpl, updateTaskAtomicImpl, getWorkflowPromptOverridesImpl, updateWorkflowSettingValuesImpl, rollbackConfigurationImpl, cancelActiveWorkflowWorkItemsForTaskImpl, setCompletionHandoffAcceptedMarkerImpl, reconcileLegacyAutoMergeStampsImpl, recoverExpiredMergeQueueLeasesImpl, rewriteDependentsForRemovalImpl, cleanupBranchForTaskImpl, addAttachmentImpl, deleteAttachmentImpl, registerArtifactImpl, updatePrInfoImpl, unlinkGithubIssueImpl, cleanupArchivedTasksImpl, generatePromptFromArchiveEntryImpl, listWorkflowOccupantTaskIdsImpl, evacuateCustomColumnsToLegacyImpl, listApprovedCliAutonomyAdaptersImpl, closeImpl, getActivityLogImpl } from "./task-store/task-mutation-ops.js"; import { getOrCreateForProjectImpl, listGoalCitationsImpl, atomicWriteTaskJsonWithAuditImpl, duplicateTaskImpl, listStrandedRefinementsImpl, tryClaimCheckoutImpl, evaluateWorkflowMovePoliciesImpl, recordRunAuditEventImpl, getRunAuditEventsImpl, getWorkflowParitySummaryImpl, dequeueMergeQueueOnColumnExitImpl, updateIssueInfoImpl, listWorkflowStepsImpl, getWorkflowStepImpl, createWorkflowDefinitionImpl, countActiveInCapacitySlotSyncImpl, countActiveInCapacitySlotAsyncImpl, generateSpecifiedPromptImpl, recordActivityImpl, getEvalStoreImpl } from "./task-store/remaining-ops-1.js"; import { markLegacyAutoMergeStampsOnceImpl, appendAgentLogImpl, importLegacyAgentLogsImpl, cleanupNoOpTaskMovedActivityRowsOnceImpl, runWorkflowColumnsIntegrityPassImpl, backfillCommitAssociationDiffStatsImpl } from "./task-store/workflow-integrity.js"; import { saveWorkflowRunBranchImpl, clearNearDuplicateReferencesToImpl, selectNextTaskForAgentImpl, pauseTaskImpl, clearLinkedAgentTaskIdsImpl, listArtifactsImpl, rehomeOccupantImpl } from "./task-store/branch-group-ops.js"; @@ -1137,7 +1137,7 @@ export class TaskStore extends EventEmitter { FNXC:SqliteFinalRemoval 2026-06-25-10:45: Route to the real implementation in reads.ts. The previous wiring called listTasksModifiedSinceImpl2 (a leftover modularization stub in - remaining-ops-2.ts) which delegated straight back to this facade method, + task-mutation-ops.ts) which delegated straight back to this facade method, causing infinite recursion in BOTH SQLite and backend modes. The real query logic lives in listTasksModifiedSinceImpl (reads.ts). */ diff --git a/packages/core/src/task-store/async-comments-attachments.ts b/packages/core/src/task-store/async-comments-attachments.ts index 2abeb55b15..fc19b3d871 100644 --- a/packages/core/src/task-store/async-comments-attachments.ts +++ b/packages/core/src/task-store/async-comments-attachments.ts @@ -627,7 +627,7 @@ export async function listArtifacts( * FNXC:Documents 2026-06-27-12:05: * Cross-task document registry query backing the dashboard `/api/documents` * list in PG backend mode (previously the sync `store.db` JOIN 500'd). Async - * equivalent of the sync `getAllDocumentsImpl` (remaining-ops-4.ts): INNER JOIN + * equivalent of the sync `getAllDocumentsImpl` (workflow-task-create-ops.ts): INNER JOIN * `task_documents` to `tasks`, filtered to live (non-soft-deleted) parent tasks * (`ACTIVE_TASK_FILTER` mirrors `TaskStore.ACTIVE_TASKS_WHERE`), newest-updated * first, returning the `TaskDocumentWithTask` shape (doc + joined task diff --git a/packages/core/src/task-store/remaining-ops-2.ts b/packages/core/src/task-store/task-mutation-ops.ts similarity index 99% rename from packages/core/src/task-store/remaining-ops-2.ts rename to packages/core/src/task-store/task-mutation-ops.ts index 9bfef1dde0..4ea4fb4e71 100644 --- a/packages/core/src/task-store/remaining-ops-2.ts +++ b/packages/core/src/task-store/task-mutation-ops.ts @@ -1,5 +1,7 @@ /** - * remaining-ops-2 operations. + * FNXC:CodeOrganization 2026-07-21-12:00: + * Domain rename from remaining-ops-2: task JSON/config writes, atomic updates, tracking reconcile, + * attachments, and related mutation helpers. * * FNXC:StoreModularization 2026-06-25-00:00: * Extracted from the monolithic packages/core/src/store.ts as a pure diff --git a/packages/core/src/task-store/remaining-ops-4.ts b/packages/core/src/task-store/workflow-task-create-ops.ts similarity index 99% rename from packages/core/src/task-store/remaining-ops-4.ts rename to packages/core/src/task-store/workflow-task-create-ops.ts index 517b061ec4..fa5bd71f25 100644 --- a/packages/core/src/task-store/remaining-ops-4.ts +++ b/packages/core/src/task-store/workflow-task-create-ops.ts @@ -1,5 +1,7 @@ /** - * remaining-ops-4 operations. + * FNXC:CodeOrganization 2026-07-21-12:00: + * Domain rename from remaining-ops-4: distributed task create, workflow step materialization, + * custom fields, work items, and goal-citation helpers. * * FNXC:StoreModularization 2026-06-25-00:00: * Extracted from the monolithic packages/core/src/store.ts as a pure diff --git a/packages/core/src/types.ts b/packages/core/src/types.ts index 63286b58ab..04930b4ffd 100644 --- a/packages/core/src/types.ts +++ b/packages/core/src/types.ts @@ -491,528 +491,136 @@ export interface TaskCommentInput { author: string; } -export type TaskReviewMode = "pull-request" | "direct"; -export type TaskReviewSource = "github-pr" | "reviewer-agent"; -export type TaskReviewDecision = "approved" | "changes-requested" | "commented" | "pending"; -export type TaskReviewVerdict = "APPROVE" | "REVISE" | "RETHINK" | "UNAVAILABLE"; -export type TaskReviewerType = "plan" | "code"; -export type TaskReviewItemStatus = "queued" | "in-progress" | "addressed" | "failed"; +// ── task-review ────────────────────────────────────────────────────────── +// FNXC:CodeOrganization 2026-07-21-12:00: Peels live in types/task-review.ts -export interface LegacyTaskReviewItem { - id: string; - source: TaskReviewSource; - status: TaskReviewItemStatus; - summary: string; - body?: string; - filePath?: string; - line?: number; - commentUrl?: string; - reviewer?: string; - createdAt: string; - updatedAt: string; - addressedAt?: string; - failedReason?: string; -} +import type { + TaskReviewMode, + TaskReviewSource, + TaskReviewDecision, + TaskReviewVerdict, + TaskReviewerType, + TaskReviewItemStatus, + PrCheckState, + ReviewAddressingStatus, + TaskReviewRefreshSource, + TaskReviewRefreshStatus, + TaskReviewItem, + LegacyTaskReviewItem, + TaskReview, + PrCheckStatus, + TaskReviewAuthor, + PrTaskReviewSummaryReviewer, + PrTaskReviewSummary, + TaskReviewStateItem, + ReviewAddressingSnapshot, + ReviewAddressingRecord, + ReviewerTaskReviewSummary, + TaskReviewState, + TaskReviewSummary, + TaskReviewDataItem, + TaskReviewData, +} from "./types/task-review.js"; +export type { + TaskReviewMode, + TaskReviewSource, + TaskReviewDecision, + TaskReviewVerdict, + TaskReviewerType, + TaskReviewItemStatus, + PrCheckState, + ReviewAddressingStatus, + TaskReviewRefreshSource, + TaskReviewRefreshStatus, + TaskReviewItem, + LegacyTaskReviewItem, + TaskReview, + PrCheckStatus, + TaskReviewAuthor, + PrTaskReviewSummaryReviewer, + PrTaskReviewSummary, + TaskReviewStateItem, + ReviewAddressingSnapshot, + ReviewAddressingRecord, + ReviewerTaskReviewSummary, + TaskReviewState, + TaskReviewSummary, + TaskReviewDataItem, + TaskReviewData, +}; -export interface TaskReview { - mode: TaskReviewMode; - source: TaskReviewSource; - decision: TaskReviewDecision; - summary?: string; - latestRefreshAt?: string; - selectedItemIds?: string[]; - items: LegacyTaskReviewItem[]; -} +// ── documents-artifacts ────────────────────────────────────────────────────────── +// FNXC:CodeOrganization 2026-07-21-12:00: Peels live in types/documents-artifacts.ts -export type PrCheckState = - | "success" - | "pending" - | "failure" - | "cancelled" - | "timed_out" - | "action_required" - | "neutral" - | "skipped" - | "stale" - | "startup_failure"; +import { + isReviewArtifact, + parseReviewArtifactsModeOverride, + resolveReviewArtifactsMode, + classifyReviewArtifactTask, + isReviewArtifactGenerationEligible, + validateDocumentKey, + buildResearchDocumentKey, + REPORT_ATTACHMENT_SOURCE, + LIVE_DEMO_ARTIFACT_MIME_TYPE, + DOCUMENT_KEY_RE, + REPO_OVERRIDE_RE, +} from "./types/documents-artifacts.js"; +export { + isReviewArtifact, + parseReviewArtifactsModeOverride, + resolveReviewArtifactsMode, + classifyReviewArtifactTask, + isReviewArtifactGenerationEligible, + validateDocumentKey, + buildResearchDocumentKey, + REPORT_ATTACHMENT_SOURCE, + LIVE_DEMO_ARTIFACT_MIME_TYPE, + DOCUMENT_KEY_RE, + REPO_OVERRIDE_RE, +}; -export interface PrCheckStatus { - name: string; - required: boolean; - state: PrCheckState; - detailsUrl?: string; - startedAt?: string; - completedAt?: string; -} - -export interface TaskReviewAuthor { - login: string; -} - -export interface PrTaskReviewSummaryReviewer { - login: string; - state: "APPROVED" | "CHANGES_REQUESTED" | "COMMENTED" | "PENDING"; - submittedAt?: string; -} - -export interface PrTaskReviewSummary { - reviewDecision: "APPROVED" | "CHANGES_REQUESTED" | "REVIEW_REQUIRED" | null; - reviewers: PrTaskReviewSummaryReviewer[]; - blockingReasons: string[]; - checks: PrCheckStatus[]; -} - -export interface TaskReviewStateItem { - id: string; - threadId?: string; - githubCommentId?: number; - path?: string; - diffSide?: string; - body: string; - author: TaskReviewAuthor; - createdAt: string; - updatedAt?: string; - state?: string; - htmlUrl?: string; - isResolved?: boolean; - source?: TaskReviewSource; - reviewType?: TaskReviewerType; - verdict?: TaskReviewVerdict; - step?: number; - summary?: string; -} - -export type ReviewAddressingStatus = "queued" | "in-progress" | "addressed" | "failed"; - -export interface ReviewAddressingSnapshot { - itemId: string; - sourceMode: "pull-request" | "reviewer-agent"; - source: "pr-review" | "reviewer-agent"; - summary: string; - body: string; - authorLogin?: string; - filePath?: string; - lineNumber?: number; - threadId?: string; - url?: string; -} - -export interface ReviewAddressingRecord { - itemId: string; - status: ReviewAddressingStatus; - selectedAt: string; - startedAt?: string; - completedAt?: string; - error?: string; - stale?: boolean; - snapshot?: ReviewAddressingSnapshot; -} - -export interface ReviewerTaskReviewSummary { - verdict?: TaskReviewVerdict; - reviewType?: TaskReviewerType; - summary?: string; -} - -export type TaskReviewRefreshSource = "manual" | "auto" | "initial-load"; -export type TaskReviewRefreshStatus = "idle" | "refreshing" | "ready" | "error"; - -export interface TaskReviewState { - source: "pull-request" | "reviewer-agent"; - lastRefreshedAt?: string; - refreshSource?: TaskReviewRefreshSource; - refreshStatus?: TaskReviewRefreshStatus; - refreshError?: string; - summary?: PrTaskReviewSummary | ReviewerTaskReviewSummary; - items: TaskReviewStateItem[]; - addressing: ReviewAddressingRecord[]; -} - -export interface TaskReviewSummary { - reviewDecision?: "APPROVED" | "CHANGES_REQUESTED" | "REVIEW_REQUIRED" | null; - reviewers?: PrTaskReviewSummaryReviewer[]; - blockingReasons?: string[]; - checks?: PrCheckStatus[]; - verdict?: TaskReviewVerdict; - reviewType?: TaskReviewerType; - summary?: string; -} - -export interface TaskReviewDataItem { - itemId: string; - sourceMode: "pull-request" | "reviewer-agent"; - title: string; - body: string; - author: string; - createdAt: string | null; - updatedAt: string | null; - url?: string; - filePath?: string; - line?: number; - threadId?: string; - reviewState?: string | null; - isResolved?: boolean; - progressStatus?: "queued" | "in-progress" | "addressed" | "failed" | null; -} - -export type TaskReviewItem = TaskReviewDataItem; - -export interface TaskReviewData { - mode: "pull-request" | "reviewer-agent"; - refreshable: boolean; - fetchedAt: string | null; - summary: TaskReviewSummary | null; - items: TaskReviewItem[]; -} - -export interface TaskDocument { - /** UUID primary key */ - id: string; - /** Task this document belongs to */ - taskId: string; - /** Document key (e.g., "plan", "notes", "research"). Alphanumeric, hyphens, underscores. */ - key: string; - /** Document body content */ - content: string; - /** Monotonically increasing revision number (starts at 1) */ - revision: number; - /** Who created/last-edited this revision: "user" | "agent" | "system" */ - author: string; - /** Optional extensible metadata (JSON object) */ - metadata?: Record; - /** ISO-8601 creation timestamp */ - createdAt: string; - /** ISO-8601 last-update timestamp */ - updatedAt: string; -} - -export interface TaskDocumentRevision { - /** Auto-increment row ID */ - id: number; - /** Task this revision belongs to */ - taskId: string; - /** Document key */ - key: string; - /** Snapshot of document content at this revision */ - content: string; - /** Revision number of this snapshot */ - revision: number; - /** Author who created this revision */ - author: string; - /** Optional metadata snapshot */ - metadata?: Record; - /** ISO-8601 timestamp when this revision was archived */ - createdAt: string; -} - -export interface TaskDocumentCreateInput { - /** Document key. Must match /^[a-zA-Z0-9_-]{1,64}$/ */ - key: string; - /** Document body content */ - content: string; - /** Author (defaults to "user" if not provided) */ - author?: string; - /** Optional extensible metadata */ - metadata?: Record; -} - -/** - * TaskDocument extended with its parent task metadata for display in the documents view. - */ -export interface TaskDocumentWithTask extends TaskDocument { - /** Title of the parent task */ - taskTitle?: string; - /** Description of the parent task */ - taskDescription?: string; - /** Column of the parent task (e.g., "triage", "todo", "in-progress", "done", "in-review", "archived") */ - taskColumn?: string; -} - -/** Supported artifact media classes for the persisted artifact registry. */ -export type ArtifactType = "document" | "image" | "video" | "audio" | "other"; - -/** - * FNXC:ReportPipeline 2026-07-19-10:00: - * Report screenshots are local image artifacts with this explicit provenance. - * Only the reference may reach report egress; screenshot pixels never do. - */ -export const REPORT_ATTACHMENT_SOURCE = "report-attachment"; - -/** - * FNXC:ArtifactRegistry 2026-06-19-22:04: - * Agents need a first-class registry for multi-type artifacts that are visible across agents and tasks. Store binary media on disk and persist only metadata plus relative URIs in SQLite so query paths stay lightweight and never inline binary bytes. - */ -export interface Artifact { - /** UUID primary key */ - id: string; - /** Artifact media class used for filtering and presentation */ - type: ArtifactType; - /** Human-readable artifact title */ - title: string; - /** Optional longer description or caption */ - description?: string; - /** Optional MIME type for inline text or binary media */ - mimeType?: string; - /** Optional content size in bytes, set from binary data when persisted on disk */ - sizeBytes?: number; - /** Relative stored path; task artifacts are anchored at the task dir, while task-less registry artifacts are anchored at `.fusion/` */ - uri?: string; - /** Optional inline text body for text/document artifacts */ - content?: string; - /** Agent, user, or system identifier that registered the artifact */ - authorId: string; - /** Class of actor that registered the artifact */ - authorType: "agent" | "user" | "system"; - /** Optional task this artifact is associated with */ - taskId?: string; - /** Optional extensible metadata (JSON object) */ - metadata?: Record; - /** ISO-8601 creation timestamp */ - createdAt: string; - /** ISO-8601 last-update timestamp */ - updatedAt: string; -} - -export interface ArtifactCreateInput { - /** Artifact media class used for filtering and presentation */ - type: ArtifactType; - /** Human-readable artifact title */ - title: string; - /** Optional longer description or caption */ - description?: string; - /** Optional MIME type for inline text or binary media */ - mimeType?: string; - /** Optional content size in bytes for inline or externally referenced content */ - sizeBytes?: number; - /** Optional relative URI when content is already stored outside SQLite */ - uri?: string; - /** Optional inline text body for text/document artifacts */ - content?: string; - /** Agent, user, or system identifier registering the artifact */ - authorId: string; - /** Class of actor registering the artifact */ - authorType: "agent" | "user" | "system"; - /** Optional task this artifact is associated with */ - taskId?: string; - /** Optional extensible metadata (JSON object) */ - metadata?: Record; - /** Optional binary payload; the store persists it on disk and records a relative URI */ - data?: Buffer; -} - -/** Artifact extended with optional parent task metadata for cross-task registry views. */ -export interface ArtifactWithTask extends Artifact { - /** Title of the parent task */ - taskTitle?: string; - /** Description of the parent task */ - taskDescription?: string; - /** Column of the parent task (e.g., "triage", "todo", "in-progress", "done", "in-review", "archived") */ - taskColumn?: string; -} - - -/* -FNXC:ReviewArtifacts 2026-07-17-12:00: -Remote-desktop producers can register a document descriptor through the existing -artifact registry by assigning this MIME type. The descriptor remains a document -in the gallery, avoiding a raw external-session link while still making the -review deliverable visible on both review surfaces. -*/ -export const LIVE_DEMO_ARTIFACT_MIME_TYPE = "application/vnd.runfusion.live-demo+json"; - -/* -FNXC:ReviewArtifacts 2026-07-17-12:00: -Review surfaces admit feature videos and explicitly marked live-demo descriptors. -Ordinary documents remain excluded; the marker uses the existing persisted -mimeType field because agent artifact registration already forwards it without -requiring a parallel schema or metadata-registration path. -*/ -export function isReviewArtifact(artifact: Pick): boolean { - return artifact.type === "video" - || (artifact.type === "document" && artifact.mimeType?.toLowerCase().split(";", 1)[0] === LIVE_DEMO_ARTIFACT_MIME_TYPE); -} - -/** Reads the persisted PROMPT.md override without adding task-store persistence. */ -export function parseReviewArtifactsModeOverride(prompt: string | undefined): ReviewArtifactsMode | undefined { - if (!prompt) return undefined; - const match = prompt.match(/^\*\*Review Artifacts:\*\*\s*(off|user-facing|on)\s*$/im); - return match?.[1]?.toLowerCase() as ReviewArtifactsMode | undefined; -} - -/** Resolves review-artifact generation policy: PROMPT header → project setting → conservative default. */ -export function resolveReviewArtifactsMode( - settings: Pick, - prompt?: string, -): ReviewArtifactsMode { - return parseReviewArtifactsModeOverride(prompt) ?? settings.reviewArtifacts ?? "off"; -} - -export type ReviewArtifactTaskClassification = "user-facing" | "backend" | "trivial"; - -/* -FNXC:ReviewArtifacts 2026-07-17-13:00: -The `user-facing` policy must be a real generation gate, not a label that -producers reinterpret. Triage may declare a task classification in PROMPT.md; -otherwise a task with the standard frontend UX contract is user-facing and all -other work conservatively remains backend. This keeps trivial/backend work from -silently producing review media while allowing `on` or the existing mode header -to explicitly opt in. -*/ -export function classifyReviewArtifactTask(prompt: string | undefined): ReviewArtifactTaskClassification { - const explicit = prompt?.match(/^\*\*Review Artifact Task Type:\*\*\s*(user-facing|backend|trivial)\s*$/im)?.[1]?.toLowerCase(); - if (explicit === "user-facing" || explicit === "backend" || explicit === "trivial") return explicit; - if (/^##\s+Frontend UX Criteria\s*$/im.test(prompt ?? "")) return "user-facing"; - return "backend"; -} - -/** - * Determines whether an automatic review-artifact producer may generate media - * for a task. A mode marker still wins policy resolution; task classification - * controls the `user-facing` mode only. - */ -export function isReviewArtifactGenerationEligible( - settings: Pick, - prompt?: string, - classification = classifyReviewArtifactTask(prompt), -): boolean { - const mode = resolveReviewArtifactsMode(settings, prompt); - return mode === "on" || (mode === "user-facing" && classification === "user-facing"); -} - -/** - * FNXC:NativeStructureEmbed 2026-07-16-12:00: - * Chat and mail share this compact reference contract so their consumers never invent - * incompatible structure identifiers. `roadmap-item` is resolved through the roadmap plugin's - * PostgreSQL-safe adapter and is missing-only because roadmap entities have no soft-delete state. - */ -export interface NativeStructureRef { - kind: "mission" | "milestone" | "research-finding" | "eval-result" | "goal" | "roadmap-item"; - id: string; - projectId?: string; -} - -/** - * FNXC:NativeStructureEmbed 2026-07-16-12:00: - * Dashboard destinations are callback/view-state based rather than HTML routes. Consumers use - * this stable descriptor with their navigation callback; it is intentionally not a URL. - * - * FNXC:NativeStructureEmbed 2026-07-19-12:30: - * Roadmap-item descriptors carry optional hierarchy context for the hosted `roadmaps` view; - * consumers pass this object to onOpen instead of manufacturing a deep-link URL. - */ -export interface NativeStructureOpenTarget { - view: "missions" | "insights" | "evals" | "goals" | "roadmaps"; - id: string; - missionId?: string; - roadmapId?: string; - milestoneId?: string; -} - -/** - * FNXC:NativeStructureEmbed 2026-07-18-18:15: - * A previewable native structure projected by the dashboard read layer. - */ -export interface NativeStructurePreviewPayload { - available: true; - kind: NativeStructureRef["kind"]; - kindLabel: string; - title: string; - excerpt: string; - openTarget: NativeStructureOpenTarget; -} - -/** - * FNXC:NativeStructureEmbed 2026-07-18-18:15: - * A native structure whose existing lifecycle state makes it unavailable for preview. - */ -export interface NativeStructureUnavailablePayload { - available: false; - kind: NativeStructureRef["kind"]; - id: string; - reason: "missing" | "soft-deleted"; -} - -/** - * FNXC:NativeStructureEmbed 2026-07-16-12:00: - * Unavailability is a typed result so shared consumers show a safe placeholder instead of - * crashing. Eval results have no archive lifecycle and therefore only return `missing`. - */ -export type NativeStructurePreviewResult = NativeStructurePreviewPayload | NativeStructureUnavailablePayload; - -/** - * Goal-citation Slice 2 success-signal surfaces where goal IDs are extracted. - */ -export type GoalCitationSurface = "agent_log" | "task_document"; - -/** - * A unique extracted goal ID and the index of its first appearance in source text. - */ -export interface GoalCitationMatch { - goalId: string; - index: number; -} - -/** - * Input payload for recording a single observed goal citation in the Slice 2 success-signal trail. - * `snippet` must be a bounded source-text substring (≤200 chars), never the full source body. - */ -export interface GoalCitationInput { - goalId: string; - agentId: string; - taskId?: string; - surface: GoalCitationSurface; - sourceRef: string; - snippet: string; - timestamp?: string; -} - -/** - * Persisted goal-citation audit row used to measure Slice 2 anchoring success signal. - * `snippet` is always a bounded substring (≤200 chars), not full source content. - */ -export interface GoalCitation extends Required> { - id: number; - taskId?: string; - timestamp: string; -} - -/** - * Filter contract for querying goal-citation success-signal rows across scanned surfaces. - * Snippet payloads remain bounded substrings (≤200 chars) of original text. - */ -export interface GoalCitationFilter { - goalId?: string; - agentId?: string; - taskId?: string; - surface?: GoalCitationSurface; - startTime?: string; - endTime?: string; - limit?: number; -} - -export const DOCUMENT_KEY_RE = /^[a-zA-Z0-9_-]{1,64}$/; - -/** Shared GitHub owner/repo slug validation for repo override inputs. */ -export const REPO_OVERRIDE_RE = /^[A-Za-z0-9._-]+\/[A-Za-z0-9._-]+$/; - -export function validateDocumentKey(key: string): void { - if (!DOCUMENT_KEY_RE.test(key)) { - throw new Error( - `Invalid document key: "${key}". Must be 1-64 characters: letters, digits, hyphens, or underscores.`, - ); - } -} - -/** Build canonical research enrichment document key from a run id. */ -export function buildResearchDocumentKey(runId: string): string { - const sanitizedRunId = runId.replace(/[^A-Za-z0-9_-]/g, ""); - if (!sanitizedRunId) { - throw new Error("Invalid research run id: sanitized run id is empty"); - } - const key = `research-${sanitizedRunId}`; - validateDocumentKey(key); - return key; -} +import type { + ArtifactType, + ReviewArtifactTaskClassification, + NativeStructurePreviewResult, + GoalCitationSurface, + TaskDocument, + TaskDocumentRevision, + TaskDocumentCreateInput, + TaskDocumentWithTask, + Artifact, + ArtifactCreateInput, + ArtifactWithTask, + NativeStructureRef, + NativeStructureOpenTarget, + NativeStructurePreviewPayload, + NativeStructureUnavailablePayload, + GoalCitationMatch, + GoalCitationInput, + GoalCitation, + GoalCitationFilter, +} from "./types/documents-artifacts.js"; +export type { + ArtifactType, + ReviewArtifactTaskClassification, + NativeStructurePreviewResult, + GoalCitationSurface, + TaskDocument, + TaskDocumentRevision, + TaskDocumentCreateInput, + TaskDocumentWithTask, + Artifact, + ArtifactCreateInput, + ArtifactWithTask, + NativeStructureRef, + NativeStructureOpenTarget, + NativeStructurePreviewPayload, + NativeStructureUnavailablePayload, + GoalCitationMatch, + GoalCitationInput, + GoalCitation, + GoalCitationFilter, +}; export interface MergeDetails { commitSha?: string; diff --git a/packages/core/src/types/documents-artifacts.ts b/packages/core/src/types/documents-artifacts.ts new file mode 100644 index 0000000000..75be76cc03 --- /dev/null +++ b/packages/core/src/types/documents-artifacts.ts @@ -0,0 +1,357 @@ +/** + * FNXC:CodeOrganization 2026-07-21-12:00: + * Task documents, artifacts, review-artifact helpers, native structure, and goal citations peeled from types.ts. + */ + +import type { ReviewArtifactsMode } from "./execution-and-ui.js"; + +export interface TaskDocument { + /** UUID primary key */ + id: string; + /** Task this document belongs to */ + taskId: string; + /** Document key (e.g., "plan", "notes", "research"). Alphanumeric, hyphens, underscores. */ + key: string; + /** Document body content */ + content: string; + /** Monotonically increasing revision number (starts at 1) */ + revision: number; + /** Who created/last-edited this revision: "user" | "agent" | "system" */ + author: string; + /** Optional extensible metadata (JSON object) */ + metadata?: Record; + /** ISO-8601 creation timestamp */ + createdAt: string; + /** ISO-8601 last-update timestamp */ + updatedAt: string; +} + +export interface TaskDocumentRevision { + /** Auto-increment row ID */ + id: number; + /** Task this revision belongs to */ + taskId: string; + /** Document key */ + key: string; + /** Snapshot of document content at this revision */ + content: string; + /** Revision number of this snapshot */ + revision: number; + /** Author who created this revision */ + author: string; + /** Optional metadata snapshot */ + metadata?: Record; + /** ISO-8601 timestamp when this revision was archived */ + createdAt: string; +} + +export interface TaskDocumentCreateInput { + /** Document key. Must match /^[a-zA-Z0-9_-]{1,64}$/ */ + key: string; + /** Document body content */ + content: string; + /** Author (defaults to "user" if not provided) */ + author?: string; + /** Optional extensible metadata */ + metadata?: Record; +} + +/** + * TaskDocument extended with its parent task metadata for display in the documents view. + */ +export interface TaskDocumentWithTask extends TaskDocument { + /** Title of the parent task */ + taskTitle?: string; + /** Description of the parent task */ + taskDescription?: string; + /** Column of the parent task (e.g., "triage", "todo", "in-progress", "done", "in-review", "archived") */ + taskColumn?: string; +} + +/** Supported artifact media classes for the persisted artifact registry. */ +export type ArtifactType = "document" | "image" | "video" | "audio" | "other"; + +/** + * FNXC:ReportPipeline 2026-07-19-10:00: + * Report screenshots are local image artifacts with this explicit provenance. + * Only the reference may reach report egress; screenshot pixels never do. + */ +export const REPORT_ATTACHMENT_SOURCE = "report-attachment"; + +/** + * FNXC:ArtifactRegistry 2026-06-19-22:04: + * Agents need a first-class registry for multi-type artifacts that are visible across agents and tasks. Store binary media on disk and persist only metadata plus relative URIs in SQLite so query paths stay lightweight and never inline binary bytes. + */ +export interface Artifact { + /** UUID primary key */ + id: string; + /** Artifact media class used for filtering and presentation */ + type: ArtifactType; + /** Human-readable artifact title */ + title: string; + /** Optional longer description or caption */ + description?: string; + /** Optional MIME type for inline text or binary media */ + mimeType?: string; + /** Optional content size in bytes, set from binary data when persisted on disk */ + sizeBytes?: number; + /** Relative stored path; task artifacts are anchored at the task dir, while task-less registry artifacts are anchored at `.fusion/` */ + uri?: string; + /** Optional inline text body for text/document artifacts */ + content?: string; + /** Agent, user, or system identifier that registered the artifact */ + authorId: string; + /** Class of actor that registered the artifact */ + authorType: "agent" | "user" | "system"; + /** Optional task this artifact is associated with */ + taskId?: string; + /** Optional extensible metadata (JSON object) */ + metadata?: Record; + /** ISO-8601 creation timestamp */ + createdAt: string; + /** ISO-8601 last-update timestamp */ + updatedAt: string; +} + +export interface ArtifactCreateInput { + /** Artifact media class used for filtering and presentation */ + type: ArtifactType; + /** Human-readable artifact title */ + title: string; + /** Optional longer description or caption */ + description?: string; + /** Optional MIME type for inline text or binary media */ + mimeType?: string; + /** Optional content size in bytes for inline or externally referenced content */ + sizeBytes?: number; + /** Optional relative URI when content is already stored outside SQLite */ + uri?: string; + /** Optional inline text body for text/document artifacts */ + content?: string; + /** Agent, user, or system identifier registering the artifact */ + authorId: string; + /** Class of actor registering the artifact */ + authorType: "agent" | "user" | "system"; + /** Optional task this artifact is associated with */ + taskId?: string; + /** Optional extensible metadata (JSON object) */ + metadata?: Record; + /** Optional binary payload; the store persists it on disk and records a relative URI */ + data?: Buffer; +} + +/** Artifact extended with optional parent task metadata for cross-task registry views. */ +export interface ArtifactWithTask extends Artifact { + /** Title of the parent task */ + taskTitle?: string; + /** Description of the parent task */ + taskDescription?: string; + /** Column of the parent task (e.g., "triage", "todo", "in-progress", "done", "in-review", "archived") */ + taskColumn?: string; +} + + +/* +FNXC:ReviewArtifacts 2026-07-17-12:00: +Remote-desktop producers can register a document descriptor through the existing +artifact registry by assigning this MIME type. The descriptor remains a document +in the gallery, avoiding a raw external-session link while still making the +review deliverable visible on both review surfaces. +*/ +export const LIVE_DEMO_ARTIFACT_MIME_TYPE = "application/vnd.runfusion.live-demo+json"; + +/* +FNXC:ReviewArtifacts 2026-07-17-12:00: +Review surfaces admit feature videos and explicitly marked live-demo descriptors. +Ordinary documents remain excluded; the marker uses the existing persisted +mimeType field because agent artifact registration already forwards it without +requiring a parallel schema or metadata-registration path. +*/ +export function isReviewArtifact(artifact: Pick): boolean { + return artifact.type === "video" + || (artifact.type === "document" && artifact.mimeType?.toLowerCase().split(";", 1)[0] === LIVE_DEMO_ARTIFACT_MIME_TYPE); +} + +/** Reads the persisted PROMPT.md override without adding task-store persistence. */ +export function parseReviewArtifactsModeOverride(prompt: string | undefined): ReviewArtifactsMode | undefined { + if (!prompt) return undefined; + const match = prompt.match(/^\*\*Review Artifacts:\*\*\s*(off|user-facing|on)\s*$/im); + return match?.[1]?.toLowerCase() as ReviewArtifactsMode | undefined; +} + +/** Resolves review-artifact generation policy: PROMPT header → project setting → conservative default. */ +export function resolveReviewArtifactsMode( + // Structural pick avoids importing ProjectSettings from types.ts (cycle). + settings: { reviewArtifacts?: ReviewArtifactsMode }, + prompt?: string, +): ReviewArtifactsMode { + return parseReviewArtifactsModeOverride(prompt) ?? settings.reviewArtifacts ?? "off"; +} + +export type ReviewArtifactTaskClassification = "user-facing" | "backend" | "trivial"; + +/* +FNXC:ReviewArtifacts 2026-07-17-13:00: +The `user-facing` policy must be a real generation gate, not a label that +producers reinterpret. Triage may declare a task classification in PROMPT.md; +otherwise a task with the standard frontend UX contract is user-facing and all +other work conservatively remains backend. This keeps trivial/backend work from +silently producing review media while allowing `on` or the existing mode header +to explicitly opt in. +*/ +export function classifyReviewArtifactTask(prompt: string | undefined): ReviewArtifactTaskClassification { + const explicit = prompt?.match(/^\*\*Review Artifact Task Type:\*\*\s*(user-facing|backend|trivial)\s*$/im)?.[1]?.toLowerCase(); + if (explicit === "user-facing" || explicit === "backend" || explicit === "trivial") return explicit; + if (/^##\s+Frontend UX Criteria\s*$/im.test(prompt ?? "")) return "user-facing"; + return "backend"; +} + +/** + * Determines whether an automatic review-artifact producer may generate media + * for a task. A mode marker still wins policy resolution; task classification + * controls the `user-facing` mode only. + */ +export function isReviewArtifactGenerationEligible( + // Structural pick avoids importing ProjectSettings from types.ts (cycle). + settings: { reviewArtifacts?: ReviewArtifactsMode }, + prompt?: string, + classification = classifyReviewArtifactTask(prompt), +): boolean { + const mode = resolveReviewArtifactsMode(settings, prompt); + return mode === "on" || (mode === "user-facing" && classification === "user-facing"); +} + +/** + * FNXC:NativeStructureEmbed 2026-07-16-12:00: + * Chat and mail share this compact reference contract so their consumers never invent + * incompatible structure identifiers. `roadmap-item` is resolved through the roadmap plugin's + * PostgreSQL-safe adapter and is missing-only because roadmap entities have no soft-delete state. + */ +export interface NativeStructureRef { + kind: "mission" | "milestone" | "research-finding" | "eval-result" | "goal" | "roadmap-item"; + id: string; + projectId?: string; +} + +/** + * FNXC:NativeStructureEmbed 2026-07-16-12:00: + * Dashboard destinations are callback/view-state based rather than HTML routes. Consumers use + * this stable descriptor with their navigation callback; it is intentionally not a URL. + * + * FNXC:NativeStructureEmbed 2026-07-19-12:30: + * Roadmap-item descriptors carry optional hierarchy context for the hosted `roadmaps` view; + * consumers pass this object to onOpen instead of manufacturing a deep-link URL. + */ +export interface NativeStructureOpenTarget { + view: "missions" | "insights" | "evals" | "goals" | "roadmaps"; + id: string; + missionId?: string; + roadmapId?: string; + milestoneId?: string; +} + +/** + * FNXC:NativeStructureEmbed 2026-07-18-18:15: + * A previewable native structure projected by the dashboard read layer. + */ +export interface NativeStructurePreviewPayload { + available: true; + kind: NativeStructureRef["kind"]; + kindLabel: string; + title: string; + excerpt: string; + openTarget: NativeStructureOpenTarget; +} + +/** + * FNXC:NativeStructureEmbed 2026-07-18-18:15: + * A native structure whose existing lifecycle state makes it unavailable for preview. + */ +export interface NativeStructureUnavailablePayload { + available: false; + kind: NativeStructureRef["kind"]; + id: string; + reason: "missing" | "soft-deleted"; +} + +/** + * FNXC:NativeStructureEmbed 2026-07-16-12:00: + * Unavailability is a typed result so shared consumers show a safe placeholder instead of + * crashing. Eval results have no archive lifecycle and therefore only return `missing`. + */ +export type NativeStructurePreviewResult = NativeStructurePreviewPayload | NativeStructureUnavailablePayload; + +/** + * Goal-citation Slice 2 success-signal surfaces where goal IDs are extracted. + */ +export type GoalCitationSurface = "agent_log" | "task_document"; + +/** + * A unique extracted goal ID and the index of its first appearance in source text. + */ +export interface GoalCitationMatch { + goalId: string; + index: number; +} + +/** + * Input payload for recording a single observed goal citation in the Slice 2 success-signal trail. + * `snippet` must be a bounded source-text substring (≤200 chars), never the full source body. + */ +export interface GoalCitationInput { + goalId: string; + agentId: string; + taskId?: string; + surface: GoalCitationSurface; + sourceRef: string; + snippet: string; + timestamp?: string; +} + +/** + * Persisted goal-citation audit row used to measure Slice 2 anchoring success signal. + * `snippet` is always a bounded substring (≤200 chars), not full source content. + */ +export interface GoalCitation extends Required> { + id: number; + taskId?: string; + timestamp: string; +} + +/** + * Filter contract for querying goal-citation success-signal rows across scanned surfaces. + * Snippet payloads remain bounded substrings (≤200 chars) of original text. + */ +export interface GoalCitationFilter { + goalId?: string; + agentId?: string; + taskId?: string; + surface?: GoalCitationSurface; + startTime?: string; + endTime?: string; + limit?: number; +} + +export const DOCUMENT_KEY_RE = /^[a-zA-Z0-9_-]{1,64}$/; + +/** Shared GitHub owner/repo slug validation for repo override inputs. */ +export const REPO_OVERRIDE_RE = /^[A-Za-z0-9._-]+\/[A-Za-z0-9._-]+$/; + +export function validateDocumentKey(key: string): void { + if (!DOCUMENT_KEY_RE.test(key)) { + throw new Error( + `Invalid document key: "${key}". Must be 1-64 characters: letters, digits, hyphens, or underscores.`, + ); + } +} + +/** Build canonical research enrichment document key from a run id. */ +export function buildResearchDocumentKey(runId: string): string { + const sanitizedRunId = runId.replace(/[^A-Za-z0-9_-]/g, ""); + if (!sanitizedRunId) { + throw new Error("Invalid research run id: sanitized run id is empty"); + } + const key = `research-${sanitizedRunId}`; + validateDocumentKey(key); + return key; +} diff --git a/packages/core/src/types/task-review.ts b/packages/core/src/types/task-review.ts new file mode 100644 index 0000000000..a1a4485c46 --- /dev/null +++ b/packages/core/src/types/task-review.ts @@ -0,0 +1,178 @@ +/** + * FNXC:CodeOrganization 2026-07-21-12:00: + * Task review and PR-review surface types peeled from types.ts. + */ + +export type TaskReviewMode = "pull-request" | "direct"; +export type TaskReviewSource = "github-pr" | "reviewer-agent"; +export type TaskReviewDecision = "approved" | "changes-requested" | "commented" | "pending"; +export type TaskReviewVerdict = "APPROVE" | "REVISE" | "RETHINK" | "UNAVAILABLE"; +export type TaskReviewerType = "plan" | "code"; +export type TaskReviewItemStatus = "queued" | "in-progress" | "addressed" | "failed"; + +export interface LegacyTaskReviewItem { + id: string; + source: TaskReviewSource; + status: TaskReviewItemStatus; + summary: string; + body?: string; + filePath?: string; + line?: number; + commentUrl?: string; + reviewer?: string; + createdAt: string; + updatedAt: string; + addressedAt?: string; + failedReason?: string; +} + +export interface TaskReview { + mode: TaskReviewMode; + source: TaskReviewSource; + decision: TaskReviewDecision; + summary?: string; + latestRefreshAt?: string; + selectedItemIds?: string[]; + items: LegacyTaskReviewItem[]; +} + +export type PrCheckState = + | "success" + | "pending" + | "failure" + | "cancelled" + | "timed_out" + | "action_required" + | "neutral" + | "skipped" + | "stale" + | "startup_failure"; + +export interface PrCheckStatus { + name: string; + required: boolean; + state: PrCheckState; + detailsUrl?: string; + startedAt?: string; + completedAt?: string; +} + +export interface TaskReviewAuthor { + login: string; +} + +export interface PrTaskReviewSummaryReviewer { + login: string; + state: "APPROVED" | "CHANGES_REQUESTED" | "COMMENTED" | "PENDING"; + submittedAt?: string; +} + +export interface PrTaskReviewSummary { + reviewDecision: "APPROVED" | "CHANGES_REQUESTED" | "REVIEW_REQUIRED" | null; + reviewers: PrTaskReviewSummaryReviewer[]; + blockingReasons: string[]; + checks: PrCheckStatus[]; +} + +export interface TaskReviewStateItem { + id: string; + threadId?: string; + githubCommentId?: number; + path?: string; + diffSide?: string; + body: string; + author: TaskReviewAuthor; + createdAt: string; + updatedAt?: string; + state?: string; + htmlUrl?: string; + isResolved?: boolean; + source?: TaskReviewSource; + reviewType?: TaskReviewerType; + verdict?: TaskReviewVerdict; + step?: number; + summary?: string; +} + +export type ReviewAddressingStatus = "queued" | "in-progress" | "addressed" | "failed"; + +export interface ReviewAddressingSnapshot { + itemId: string; + sourceMode: "pull-request" | "reviewer-agent"; + source: "pr-review" | "reviewer-agent"; + summary: string; + body: string; + authorLogin?: string; + filePath?: string; + lineNumber?: number; + threadId?: string; + url?: string; +} + +export interface ReviewAddressingRecord { + itemId: string; + status: ReviewAddressingStatus; + selectedAt: string; + startedAt?: string; + completedAt?: string; + error?: string; + stale?: boolean; + snapshot?: ReviewAddressingSnapshot; +} + +export interface ReviewerTaskReviewSummary { + verdict?: TaskReviewVerdict; + reviewType?: TaskReviewerType; + summary?: string; +} + +export type TaskReviewRefreshSource = "manual" | "auto" | "initial-load"; +export type TaskReviewRefreshStatus = "idle" | "refreshing" | "ready" | "error"; + +export interface TaskReviewState { + source: "pull-request" | "reviewer-agent"; + lastRefreshedAt?: string; + refreshSource?: TaskReviewRefreshSource; + refreshStatus?: TaskReviewRefreshStatus; + refreshError?: string; + summary?: PrTaskReviewSummary | ReviewerTaskReviewSummary; + items: TaskReviewStateItem[]; + addressing: ReviewAddressingRecord[]; +} + +export interface TaskReviewSummary { + reviewDecision?: "APPROVED" | "CHANGES_REQUESTED" | "REVIEW_REQUIRED" | null; + reviewers?: PrTaskReviewSummaryReviewer[]; + blockingReasons?: string[]; + checks?: PrCheckStatus[]; + verdict?: TaskReviewVerdict; + reviewType?: TaskReviewerType; + summary?: string; +} + +export interface TaskReviewDataItem { + itemId: string; + sourceMode: "pull-request" | "reviewer-agent"; + title: string; + body: string; + author: string; + createdAt: string | null; + updatedAt: string | null; + url?: string; + filePath?: string; + line?: number; + threadId?: string; + reviewState?: string | null; + isResolved?: boolean; + progressStatus?: "queued" | "in-progress" | "addressed" | "failed" | null; +} + +export type TaskReviewItem = TaskReviewDataItem; + +export interface TaskReviewData { + mode: "pull-request" | "reviewer-agent"; + refreshable: boolean; + fetchedAt: string | null; + summary: TaskReviewSummary | null; + items: TaskReviewItem[]; +} diff --git a/scripts/line-count-baseline.json b/scripts/line-count-baseline.json index 2abf69f005..5a7cdaf30c 100644 --- a/scripts/line-count-baseline.json +++ b/scripts/line-count-baseline.json @@ -1,36 +1,36 @@ { "packages/cli/src/__tests__/extension.test.ts": 4491, - "packages/cli/src/bin.ts": 2338, + "packages/cli/src/bin.ts": 2321, "packages/cli/src/commands/__tests__/serve.test.ts": 2313, "packages/cli/src/commands/__tests__/task.test.ts": 3644, "packages/cli/src/commands/dashboard-tui/app.tsx": 4697, - "packages/cli/src/commands/dashboard.ts": 3729, + "packages/cli/src/commands/dashboard.ts": 3739, "packages/cli/src/commands/task.ts": 2372, "packages/cli/src/extension.ts": 5694, - "packages/core/src/__tests__/postgres/schema-applier.test.ts": 2435, + "packages/core/src/__tests__/postgres/schema-applier.test.ts": 2457, "packages/core/src/agent-store.ts": 3484, "packages/core/src/async-mission-store-queries.ts": 2093, "packages/core/src/central-core.ts": 4486, - "packages/core/src/index.gate.ts": 2248, - "packages/core/src/index.ts": 2610, - "packages/core/src/mission-store.ts": 4369, - "packages/core/src/postgres/schema/project.ts": 2218, - "packages/core/src/postgres/sqlite-migrator.ts": 2326, - "packages/core/src/store.ts": 2817, - "packages/core/src/types.ts": 6263, - "packages/dashboard/app/App.tsx": 2110, + "packages/core/src/index.gate.ts": 2256, + "packages/core/src/index.ts": 2618, + "packages/core/src/mission-store.ts": 4361, + "packages/core/src/postgres/schema/project.ts": 2227, + "packages/core/src/postgres/sqlite-migrator.ts": 2308, + "packages/core/src/store.ts": 2820, + "packages/core/src/types.ts": 5872, + "packages/dashboard/app/App.tsx": 2136, "packages/dashboard/app/components/AgentDetailView.tsx": 5567, "packages/dashboard/app/components/AgentsView.tsx": 2254, "packages/dashboard/app/components/ChatView.tsx": 4043, "packages/dashboard/app/components/GitHubImportModal.tsx": 2481, "packages/dashboard/app/components/GitManagerModal.tsx": 3560, - "packages/dashboard/app/components/ListView.tsx": 3118, + "packages/dashboard/app/components/ListView.tsx": 3152, "packages/dashboard/app/components/MissionManager.tsx": 5149, "packages/dashboard/app/components/ModelOnboardingModal.tsx": 3594, - "packages/dashboard/app/components/PlanningModeModal.tsx": 4538, + "packages/dashboard/app/components/PlanningModeModal.tsx": 4697, "packages/dashboard/app/components/QuickEntryBox.tsx": 2562, - "packages/dashboard/app/components/SettingsModal.tsx": 5258, - "packages/dashboard/app/components/TaskCard.tsx": 3857, + "packages/dashboard/app/components/SettingsModal.tsx": 5257, + "packages/dashboard/app/components/TaskCard.tsx": 3896, "packages/dashboard/app/components/TaskDetailModal.tsx": 6528, "packages/dashboard/app/components/TerminalModal.tsx": 3322, "packages/dashboard/app/components/WorkflowNodeEditor.tsx": 5685, @@ -38,7 +38,7 @@ "packages/dashboard/app/components/__tests__/App.test.tsx": 4663, "packages/dashboard/app/components/__tests__/Board.test.tsx": 2107, "packages/dashboard/app/components/__tests__/ChatView.mobile.test.tsx": 2436, - "packages/dashboard/app/components/__tests__/GitHubImportModal.test.tsx": 3171, + "packages/dashboard/app/components/__tests__/GitHubImportModal.test.tsx": 3221, "packages/dashboard/app/components/__tests__/GitManagerModal.test.tsx": 3790, "packages/dashboard/app/components/__tests__/ListView.test.tsx": 5380, "packages/dashboard/app/components/__tests__/MailboxView.test.tsx": 2646, @@ -46,7 +46,7 @@ "packages/dashboard/app/components/__tests__/NewTaskModal.test.tsx": 2081, "packages/dashboard/app/components/__tests__/QuickEntryBox.test.tsx": 5433, "packages/dashboard/app/components/__tests__/SettingsModal.general.test.tsx": 2166, - "packages/dashboard/app/components/__tests__/TaskCard.test.tsx": 7640, + "packages/dashboard/app/components/__tests__/TaskCard.test.tsx": 7835, "packages/dashboard/app/components/__tests__/TaskChatTab.test.tsx": 3175, "packages/dashboard/app/components/__tests__/TaskDetailModal.inline-editing-and-integrations.test.tsx": 3438, "packages/dashboard/app/components/__tests__/TaskDetailModal.rendering.test.tsx": 2854, @@ -63,44 +63,45 @@ "packages/dashboard/src/__tests__/github.test.ts": 2467, "packages/dashboard/src/__tests__/routes-auth.test.ts": 5510, "packages/dashboard/src/__tests__/routes-automation.test.ts": 3089, - "packages/dashboard/src/__tests__/routes-github.test.ts": 3900, + "packages/dashboard/src/__tests__/routes-github.test.ts": 3911, "packages/dashboard/src/__tests__/routes-planning.test.ts": 5379, "packages/dashboard/src/__tests__/routes-tasks.test.ts": 3024, - "packages/dashboard/src/__tests__/usage.test.ts": 5261, + "packages/dashboard/src/__tests__/usage.test.ts": 5427, "packages/dashboard/src/chat.ts": 3134, "packages/dashboard/src/github.ts": 5112, "packages/dashboard/src/mission-routes.ts": 3924, - "packages/dashboard/src/planning.ts": 3640, + "packages/dashboard/src/planning.ts": 3645, "packages/dashboard/src/routes.ts": 2043, "packages/dashboard/src/routes/register-git-github.ts": 6342, "packages/dashboard/src/routes/register-settings-memory-routes.ts": 2403, - "packages/dashboard/src/routes/register-task-workflow-routes.ts": 5628, - "packages/dashboard/src/server.ts": 2890, - "packages/dashboard/src/usage.ts": 2398, + "packages/dashboard/src/routes/register-task-workflow-routes.ts": 5641, + "packages/dashboard/src/server.ts": 2883, + "packages/dashboard/src/usage.ts": 2435, "packages/engine/src/__tests__/cron-runner.test.ts": 2334, "packages/engine/src/__tests__/executor-prompt.test.ts": 2838, "packages/engine/src/__tests__/executor-worktree.test.ts": 2946, - "packages/engine/src/__tests__/heartbeat-executor.test.ts": 4511, - "packages/engine/src/__tests__/heartbeat-scheduler.test.ts": 3327, + "packages/engine/src/__tests__/heartbeat-executor.test.ts": 4545, + "packages/engine/src/__tests__/heartbeat-scheduler.test.ts": 3346, "packages/engine/src/__tests__/merger-merge-lifecycle.test.ts": 3697, "packages/engine/src/__tests__/merger-verification.test.ts": 3177, "packages/engine/src/__tests__/mission-execution-loop.test.ts": 2984, - "packages/engine/src/__tests__/pi-create-fn-agent.test.ts": 3167, - "packages/engine/src/__tests__/project-engine.test.ts": 3691, - "packages/engine/src/__tests__/restart.integration.test.ts": 2199, + "packages/engine/src/__tests__/pi-create-fn-agent.test.ts": 3292, + "packages/engine/src/__tests__/project-engine.test.ts": 3725, + "packages/engine/src/__tests__/restart.integration.test.ts": 2267, "packages/engine/src/__tests__/self-healing.test.ts": 11840, "packages/engine/src/__tests__/step-session-executor.test.ts": 3226, - "packages/engine/src/__tests__/triage.test.ts": 7002, + "packages/engine/src/__tests__/triage.test.ts": 7042, "packages/engine/src/agent-heartbeat.ts": 5422, - "packages/engine/src/agent-tools.ts": 5433, - "packages/engine/src/executor.ts": 20076, - "packages/engine/src/merger-ai.ts": 2401, - "packages/engine/src/merger.ts": 11239, - "packages/engine/src/pi.ts": 2836, - "packages/engine/src/project-engine.ts": 5410, - "packages/engine/src/scheduler.ts": 3584, + "packages/engine/src/agent-tools.ts": 5437, + "packages/engine/src/executor.ts": 20411, + "packages/engine/src/merger-ai.ts": 2445, + "packages/engine/src/merger.ts": 11244, + "packages/engine/src/pi.ts": 2900, + "packages/engine/src/project-engine.ts": 5527, + "packages/engine/src/runtimes/in-process-runtime.ts": 2160, + "packages/engine/src/scheduler.ts": 3666, "packages/engine/src/self-healing.ts": 12888, - "packages/engine/src/triage.ts": 3479, + "packages/engine/src/triage.ts": 3645, "packages/pi-claude-cli/src/__tests__/provider.test.ts": 2028, "plugins/fusion-plugin-roadmap/src/dashboard/RoadmapsView.tsx": 2583, "scripts/__tests__/test-changed.test.mjs": 2134