Files
harness-tools/extensions/pi-workflow/domain.ts
T

466 lines
12 KiB
TypeScript

export const PROFILE_ROLES = [
"discover",
"design",
"prototype",
"implement",
"verify",
"review",
] as const;
export type ProfileRole = (typeof PROFILE_ROLES)[number];
export type AssignmentStageKind = ProfileRole;
export type WorkflowStageKind =
| "initialize"
| AssignmentStageKind
| "remediate"
| "complete"
| `custom:${string}`;
export const REASONING_EFFORTS = [
"off",
"minimal",
"low",
"medium",
"high",
"xhigh",
"max",
] as const;
export type ReasoningEffort = (typeof REASONING_EFFORTS)[number];
export interface ProfileSelection {
stages: AssignmentStageKind[];
anyTags: string[];
allTags: string[];
pathGlobs: string[];
keywords: string[];
}
export interface AgentProfile {
name: string;
description: string;
roles: ProfileRole[];
model?: string;
provider?: string;
effort?: ReasoningEffort;
tools?: string[];
selection: ProfileSelection;
instructions: string;
source: "bundled" | "project";
sourcePath: string;
resolvedPath: string;
digest: string;
}
export interface ProfileDiagnostic {
sourcePath: string;
severity: "warning" | "error";
message: string;
}
export interface ProfileCatalog {
profiles: AgentProfile[];
diagnostics: ProfileDiagnostic[];
}
export interface WorkflowFacts {
stage: AssignmentStageKind;
taskKind: "feature" | "analysis";
requestTerms: string[];
candidatePaths: string[];
languages: string[];
tags: string[];
riskTags: string[];
}
export interface EligibilityResult {
profile: AgentProfile;
eligible: boolean;
reasons: string[];
specificity: number;
}
export type RunStatus =
| "running"
| "waiting"
| "completed"
| "failed"
| "aborted";
export type AssignmentStatus =
| "queued"
| "running"
| "succeeded"
| "failed"
| "interrupted";
export type StageStatus =
| "pending"
| "running"
| "waiting"
| "completed"
| "failed";
export type GateKind =
| "questions"
| "plan-approval"
| "prototype-acceptance"
| "implementation-context"
| "recovery";
export type UiImpact = "none" | "minor" | "non-trivial";
export type GateDecision =
| { kind: "answer-questions"; answers: Record<string, string> }
| {
kind: "approve-plan";
approved: true;
acceptedUiImpact: UiImpact;
comment?: string;
}
| { kind: "revise-plan"; feedback: string }
| {
kind: "accept-prototype";
candidateId: string;
candidateRevision: number;
browserReviewed: true;
feedback?: string;
}
| { kind: "refine-prototypes"; feedback: string }
| {
kind: "select-implementation-context";
strategy:
| "fresh-session"
| "smart-compact"
| "current-session";
}
| {
kind: "report-context-preparation";
preparationId: string;
planRevision: number;
method: "smart_compact";
outcome: "unavailable" | "succeeded" | "no-op";
}
| {
kind: "report-context-preparation";
preparationId: string;
planRevision: number;
method: "smart_compact";
outcome: "failed";
error: string;
}
| { kind: "recover"; resolution: "retry" | "inspect" | "abort" };
export interface RunLease {
processId: number;
activeParentSessionId: string;
heartbeatAt: number;
worktreeIdentity?: string;
}
export interface WorkflowWorkspace {
state: "not-required" | "creating" | "ready" | "failed";
sourceRoot: string;
root?: string;
branch?: string;
baseHead?: string;
error?: string;
}
export interface WorkflowStage {
id: string;
kind: WorkflowStageKind;
status: StageStatus;
startedAt?: number;
finishedAt?: number;
output?: unknown;
}
export interface WorkflowGate {
id: string;
kind: GateKind;
status: "open" | "satisfied" | "rejected";
revision: number;
openedAt: number;
payload?: unknown;
}
export interface WorkflowDecision {
gateId: string;
revision: number;
idempotencyKey: string;
decidedAt: number;
value: GateDecision;
}
export interface ProfileSelectionEvidence {
profileName: string;
profileDigest: string;
eligible: boolean;
reasons: string[];
specificity: number;
}
export interface AssignmentSelection {
eligibleProfiles: ProfileSelectionEvidence[];
selectedProfileName: string;
rationale: string;
}
export interface StageEligibilityEvidence {
role: ProfileRole;
candidates: ProfileSelectionEvidence[];
}
export interface AssignmentTranscriptArtifact {
path: string;
digest: string;
}
export interface AssignmentGitState {
root: string;
branch: string;
head: string;
commonDirectory: string;
gitDirectory: string;
worktreeListDigest: string;
worktreeMetadataDigest: string;
status: string;
changedPaths: string[];
pathStatuses: Record<string, string>;
pathDigests: Record<string, string>;
}
export interface VerificationCommand {
id: string;
executable: string;
args: string[];
ownedPaths: string[];
kind: "test" | "typecheck" | "lint" | "build" | "browser" | "generation";
status:
| "pending"
| "running"
| "succeeded"
| "failed"
| "interrupted"
| "superseded";
exitCode?: number;
stdout?: string;
stderr?: string;
error?: string;
}
export interface VerificationEvidence {
commands: VerificationCommand[];
focusedBehavioralTests: boolean;
verifierSucceeded: boolean;
}
export interface ReviewAxis {
verdict: "approved" | "changes_requested";
findings: Array<{ severity: string; message: string; path?: string }>;
}
export interface ReviewVerdict {
specification: ReviewAxis;
standards: ReviewAxis;
}
export interface WorkflowAssignment {
id: string;
stageId: string;
role: ProfileRole | "remediate";
profileName: string;
profileDigest: string;
selection: AssignmentSelection;
status: AssignmentStatus;
mutates: boolean;
ownedPaths: string[];
scriptRequestId?: number;
scriptRequestFingerprint?: string;
executionToken?: string;
childSessionId?: string;
effectiveProvider?: string;
effectiveModel?: string;
effectiveEffort?: string;
contextCapacity?: number;
transcriptArtifact?: AssignmentTranscriptArtifact;
gitBefore?: AssignmentGitState;
gitAfter?: AssignmentGitState;
result?: unknown;
error?: string;
}
export interface PrototypeCandidate {
id: string;
revision: number;
assignmentId: string;
profileName: string;
status: "succeeded";
path: string;
route: string;
structuralRationale: string;
responsiveChecks: string[];
browserEvidence: string[];
launchInstructions: string;
visualDigest: string;
artifactPath: string;
retainedArtifacts: string[];
}
export interface DeliveryRecord {
key: string;
state: "pending" | "sent";
createdAt: number;
sentAt?: number;
}
export interface WorkflowReportArtifact {
path: string;
digest: string;
revision: number;
}
export interface PlanArtifact {
path: string;
revision: number;
digest: string;
}
export interface ImplementationContextState {
gateId: string;
runRevision: number;
planRevision: number;
preparationId: string;
activeParentSessionId: string;
sourceParentSessionId?: string;
sourceSessionFile?: string;
targetSessionFile?: string;
claimedAt?: number;
intentCreatedAt?: number;
intentExpiresAt?: number;
strategy?: "fresh-session" | "smart-compact" | "current-session";
status:
| "waiting"
| "handoff-pending"
| "claimed-target-awaiting-withSession"
| "smart-compact-pending"
| "normal-compact-pending"
| "normal-compact-running"
| "ready"
| "failed";
method?:
| "fresh-session"
| "smart_compact"
| "normal-compact"
| "unchanged";
outcome?: "unavailable" | "succeeded" | "no-op" | "failed";
error?: string;
}
export interface WorkflowError {
code: string;
message: string;
stageId?: string;
assignmentId?: string;
}
export type CommandContinuation =
| "none"
| "design"
| "prototype"
| "implementation"
| "recovery";
export interface CommandReceipt {
key: string;
action: "decide" | "abort";
fingerprint: string;
status: "pending" | "completed";
acceptedRevision: number;
continuation?: CommandContinuation;
result?: unknown;
}
export type FeatureScriptInsertionPoint =
| "before-design"
| "after-design"
| "before-review";
export interface WorkflowScriptState {
source: string;
args?: unknown;
insertionPoint?: FeatureScriptInsertionPoint;
activeToolNames: string[];
status: "pending" | "running" | "completed" | "failed";
stages: string[];
executed: boolean;
result?: unknown;
error?: string;
}
export interface WorkflowRun {
runId: string;
revision: number;
mode: "feature" | "analysis";
script?: WorkflowScriptState;
status: RunStatus;
background: boolean;
request: string;
featureName?: string;
parentSessionIds: string[];
planArtifact?: PlanArtifact;
reportArtifact?: WorkflowReportArtifact;
implementationContext?: ImplementationContextState;
lease?: RunLease;
workspace: WorkflowWorkspace;
effectiveConfig: WorkflowConfig;
pinnedProfiles: AgentProfile[];
preflightEligibility: StageEligibilityEvidence[];
facts: WorkflowFacts;
stages: WorkflowStage[];
gates: WorkflowGate[];
decisions: WorkflowDecision[];
assignments: WorkflowAssignment[];
prototypeCandidates: PrototypeCandidate[];
prototypeCleanupCompleted: boolean;
deliveries: DeliveryRecord[];
commandReceipts: CommandReceipt[];
uiImpact?: UiImpact;
uiImpactEvidence: string[];
affectedUiSurfaces: string[];
mutatorProfileNames: string[];
reviewCycles: number;
verificationCycles: number;
verificationEvidence?: VerificationEvidence;
reviewVerdict?: ReviewVerdict;
error?: WorkflowError;
}
export const ANALYSIS_TOOL_ALLOWLIST = ["read", "grep", "find", "ls"] as const;
export const MAX_CONCURRENCY = 32;
export const MAX_ASSIGNMENTS_PER_RUN = 1_024;
export const MAX_REVIEW_CYCLES = 20;
export interface WorkflowConfig {
concurrency: {
default: number;
ceiling: number;
};
maxAssignmentsPerRun: number;
maxReviewCycles: number;
analysisTools: string[];
worktrees: {
root: string;
};
}
export const DEFAULT_WORKFLOW_CONFIG: WorkflowConfig = {
concurrency: {
default: 8,
ceiling: MAX_CONCURRENCY,
},
maxAssignmentsPerRun: 128,
maxReviewCycles: 3,
analysisTools: [...ANALYSIS_TOOL_ALLOWLIST],
worktrees: {
root: ".worktrees",
},
};