refactor: add numbered step comments to 20 complex sequential functions
- Add // N. Description steps to temporal layer (client, activities, workflows) - Add steps to AI layer (claude-executor: runClaudePrompt, buildMcpServers) - Add steps to services layer (prompt-manager, config-parser, git-manager) - Add steps to audit layer (metrics-tracker, audit-session) - Update CLAUDE.md comment guidelines with clearer numbered-step vs section-divider guidance
This commit is contained in:
@@ -117,17 +117,17 @@ async function runAgentActivity(
|
||||
try {
|
||||
const logger = createActivityLogger();
|
||||
|
||||
// Build session metadata and get/create container
|
||||
// 1. Build session metadata and get/create container
|
||||
const sessionMetadata = buildSessionMetadata(input);
|
||||
const container = getOrCreateContainer(workflowId, sessionMetadata);
|
||||
|
||||
// Create audit session for THIS agent execution
|
||||
// 2. Create audit session for THIS agent execution
|
||||
// NOTE: Each agent needs its own AuditSession because AuditSession uses
|
||||
// instance state (currentAgentName) that cannot be shared across parallel agents
|
||||
const auditSession = new AuditSession(sessionMetadata);
|
||||
await auditSession.initialize(workflowId);
|
||||
|
||||
// Execute agent via service (throws PentestError on failure)
|
||||
// 3. Execute agent via service (throws PentestError on failure)
|
||||
const endResult = await container.agentExecution.executeOrThrow(
|
||||
agentName,
|
||||
{
|
||||
@@ -141,7 +141,7 @@ async function runAgentActivity(
|
||||
logger
|
||||
);
|
||||
|
||||
// Success - return metrics
|
||||
// 4. Return metrics
|
||||
return {
|
||||
durationMs: Date.now() - startTime,
|
||||
inputTokens: null,
|
||||
@@ -325,6 +325,7 @@ export async function loadResumeState(
|
||||
expectedUrl: string,
|
||||
expectedRepoPath: string
|
||||
): Promise<ResumeState> {
|
||||
// 1. Validate workspace exists
|
||||
const sessionPath = path.join('./audit-logs', workspaceName, 'session.json');
|
||||
|
||||
const exists = await fileExists(sessionPath);
|
||||
@@ -335,6 +336,7 @@ export async function loadResumeState(
|
||||
);
|
||||
}
|
||||
|
||||
// 2. Parse session.json and validate URL match
|
||||
let session: SessionJson;
|
||||
try {
|
||||
session = await readJson<SessionJson>(sessionPath);
|
||||
@@ -353,6 +355,7 @@ export async function loadResumeState(
|
||||
);
|
||||
}
|
||||
|
||||
// 3. Cross-check agent status with deliverables on disk
|
||||
const completedAgents: string[] = [];
|
||||
const agents = session.metrics.agents;
|
||||
|
||||
@@ -375,6 +378,7 @@ export async function loadResumeState(
|
||||
completedAgents.push(agentName);
|
||||
}
|
||||
|
||||
// 4. Collect git checkpoints and validate at least one exists
|
||||
const checkpoints = completedAgents
|
||||
.map((name) => agents[name]?.checkpoint)
|
||||
.filter((hash): hash is string => hash != null);
|
||||
@@ -395,9 +399,11 @@ export async function loadResumeState(
|
||||
);
|
||||
}
|
||||
|
||||
// 5. Find the most recent checkpoint commit
|
||||
const checkpointHash = await findLatestCommit(expectedRepoPath, checkpoints);
|
||||
const originalWorkflowId = session.session.originalWorkflowId || session.session.id;
|
||||
|
||||
// 6. Log summary and return resume state
|
||||
const logger = createActivityLogger();
|
||||
logger.info('Resume state loaded', {
|
||||
workspace: workspaceName,
|
||||
@@ -533,11 +539,12 @@ export async function logWorkflowComplete(
|
||||
const { repoPath, workflowId } = input;
|
||||
const sessionMetadata = buildSessionMetadata(input);
|
||||
|
||||
// 1. Initialize audit session and mark final status
|
||||
const auditSession = new AuditSession(sessionMetadata);
|
||||
await auditSession.initialize(workflowId);
|
||||
await auditSession.updateSessionStatus(summary.status);
|
||||
|
||||
// Use cumulative metrics from session.json
|
||||
// 2. Load cumulative metrics from session.json
|
||||
const sessionData = (await auditSession.getMetrics()) as {
|
||||
metrics: {
|
||||
total_duration_ms: number;
|
||||
@@ -546,7 +553,7 @@ export async function logWorkflowComplete(
|
||||
};
|
||||
};
|
||||
|
||||
// Fill in metrics for skipped agents
|
||||
// 3. Fill in metrics for skipped agents (resumed from previous run)
|
||||
const agentMetrics = { ...summary.agentMetrics };
|
||||
for (const agentName of summary.completedAgents) {
|
||||
if (!agentMetrics[agentName]) {
|
||||
@@ -560,15 +567,18 @@ export async function logWorkflowComplete(
|
||||
}
|
||||
}
|
||||
|
||||
// 4. Build cumulative summary with cross-run totals
|
||||
const cumulativeSummary: WorkflowSummary = {
|
||||
...summary,
|
||||
totalDurationMs: sessionData.metrics.total_duration_ms,
|
||||
totalCostUsd: sessionData.metrics.total_cost_usd,
|
||||
agentMetrics,
|
||||
};
|
||||
|
||||
// 5. Write completion entry to workflow.log
|
||||
await auditSession.logWorkflowComplete(cumulativeSummary);
|
||||
|
||||
// Copy deliverables to audit-logs
|
||||
// 6. Copy deliverables to audit-logs
|
||||
try {
|
||||
await copyDeliverablesToAudit(sessionMetadata, repoPath);
|
||||
} catch (copyErr) {
|
||||
@@ -578,6 +588,6 @@ export async function logWorkflowComplete(
|
||||
});
|
||||
}
|
||||
|
||||
// Clean up container
|
||||
// 7. Clean up container
|
||||
removeContainer(workflowId);
|
||||
}
|
||||
|
||||
@@ -262,11 +262,13 @@ async function resolveWorkspace(
|
||||
console.log('=== RESUME MODE ===');
|
||||
console.log(`Workspace: ${workspace}\n`);
|
||||
|
||||
// 1. Terminate any running workflows from previous attempts
|
||||
const terminatedWorkflows = await terminateExistingWorkflows(client, workspace);
|
||||
if (terminatedWorkflows.length > 0) {
|
||||
console.log(`Terminated ${terminatedWorkflows.length} previous workflow(s)\n`);
|
||||
}
|
||||
|
||||
// 2. Validate URL matches the workspace
|
||||
const session = await readJson<SessionJson>(sessionPath);
|
||||
if (session.session.webUrl !== args.webUrl) {
|
||||
console.error('ERROR: URL mismatch with workspace');
|
||||
@@ -275,6 +277,8 @@ async function resolveWorkspace(
|
||||
process.exit(1);
|
||||
}
|
||||
|
||||
// 3. Generate a new workflow ID scoped to this resume attempt
|
||||
// 4. Return resolution with isResume=true so downstream uses resume logic
|
||||
return {
|
||||
workflowId: `${workspace}_resume_${Date.now()}`,
|
||||
sessionId: workspace,
|
||||
@@ -371,9 +375,11 @@ async function waitForWorkflowResult(
|
||||
}, 30000);
|
||||
|
||||
try {
|
||||
// 1. Block until workflow completes
|
||||
const result = await handle.result();
|
||||
clearInterval(progressInterval);
|
||||
|
||||
// 2. Display run metrics
|
||||
console.log('\nPipeline completed successfully!');
|
||||
if (result.summary) {
|
||||
console.log(`Duration: ${Math.floor(result.summary.totalDurationMs / 1000)}s`);
|
||||
@@ -381,6 +387,7 @@ async function waitForWorkflowResult(
|
||||
console.log(`Total turns: ${result.summary.totalTurns}`);
|
||||
console.log(`Run cost: $${result.summary.totalCostUsd.toFixed(4)}`);
|
||||
|
||||
// 3. Show cumulative cost across all resume attempts
|
||||
if (workspace.isResume) {
|
||||
try {
|
||||
const session = await readJson<SessionJson>(
|
||||
@@ -402,9 +409,11 @@ async function waitForWorkflowResult(
|
||||
// === Main Entry Point ===
|
||||
|
||||
async function startPipeline(): Promise<void> {
|
||||
// 1. Parse CLI args and display splash
|
||||
const args = parseCliArgs(process.argv.slice(2));
|
||||
await displaySplashScreen();
|
||||
|
||||
// 2. Connect to Temporal server
|
||||
const address = process.env.TEMPORAL_ADDRESS || 'localhost:7233';
|
||||
console.log(`Connecting to Temporal at ${address}...`);
|
||||
|
||||
@@ -412,9 +421,11 @@ async function startPipeline(): Promise<void> {
|
||||
const client = new Client({ connection });
|
||||
|
||||
try {
|
||||
// 3. Resolve workspace (new or resume) and build pipeline input
|
||||
const workspace = await resolveWorkspace(client, args);
|
||||
const input = buildPipelineInput(args, workspace);
|
||||
|
||||
// 4. Start the Temporal workflow
|
||||
const handle = await client.workflow.start<(input: PipelineInput) => Promise<PipelineState>>(
|
||||
'pentestPipelineWorkflow',
|
||||
{
|
||||
@@ -424,6 +435,7 @@ async function startPipeline(): Promise<void> {
|
||||
}
|
||||
);
|
||||
|
||||
// 5. Display info and optionally wait for completion
|
||||
displayWorkflowInfo(args, workspace);
|
||||
|
||||
if (args.waitForCompletion) {
|
||||
|
||||
@@ -147,12 +147,14 @@ export async function pentestPipelineWorkflow(
|
||||
let resumeState: ResumeState | null = null;
|
||||
|
||||
if (input.resumeFromWorkspace) {
|
||||
// 1. Load resume state (validates workspace, cross-checks deliverables)
|
||||
resumeState = await a.loadResumeState(
|
||||
input.resumeFromWorkspace,
|
||||
input.webUrl,
|
||||
input.repoPath
|
||||
);
|
||||
|
||||
// 2. Restore git workspace and clean up incomplete deliverables
|
||||
const incompleteAgents = ALL_AGENTS.filter(
|
||||
(agentName) => !resumeState!.completedAgents.includes(agentName)
|
||||
) as AgentName[];
|
||||
@@ -163,6 +165,7 @@ export async function pentestPipelineWorkflow(
|
||||
incompleteAgents
|
||||
);
|
||||
|
||||
// 3. Short-circuit if all agents already completed
|
||||
if (resumeState.completedAgents.length === ALL_AGENTS.length) {
|
||||
log.info(`All ${ALL_AGENTS.length} agents already completed. Nothing to resume.`);
|
||||
state.status = 'completed';
|
||||
@@ -171,6 +174,7 @@ export async function pentestPipelineWorkflow(
|
||||
return state;
|
||||
}
|
||||
|
||||
// 4. Record this resume attempt in session.json and workflow.log
|
||||
await a.recordResumeAttempt(
|
||||
activityInput,
|
||||
input.terminatedWorkflows || [],
|
||||
@@ -317,6 +321,7 @@ export async function pentestPipelineWorkflow(
|
||||
const vulnAgentName = `${vulnType}-vuln`;
|
||||
const exploitAgentName = `${vulnType}-exploit`;
|
||||
|
||||
// 1. Run vulnerability analysis (or skip if resumed)
|
||||
let vulnMetrics: AgentMetrics | null = null;
|
||||
if (!shouldSkip(vulnAgentName)) {
|
||||
vulnMetrics = await runVulnAgent();
|
||||
@@ -324,8 +329,10 @@ export async function pentestPipelineWorkflow(
|
||||
log.info(`Skipping ${vulnAgentName} (already complete)`);
|
||||
}
|
||||
|
||||
// 2. Check exploitation queue for actionable findings
|
||||
const decision = await a.checkExploitationQueue(activityInput, vulnType);
|
||||
|
||||
// 3. Conditionally run exploitation agent
|
||||
let exploitMetrics: AgentMetrics | null = null;
|
||||
if (decision.shouldExploit) {
|
||||
if (!shouldSkip(exploitAgentName)) {
|
||||
|
||||
Reference in New Issue
Block a user