Complete Execution Flow
From Genome to Generated Project
This page traces the complete journey from when a user runs architech new to a fully generated project on disk.
The High-Level Flow
Let's trace each step in detail.
Step 1: User Command
$ architech new my-saas.genome.ts --verboseWhat Happens:
- Commander.js parses command line
new.tscommand handler invoked- Genome file path resolved
// src/commands/new.ts
export function createNewCommand(): Command {
command
.argument('<genome-file>', 'Path to .genome.ts file')
.action(async (genomeFile: string, options) => {
const genomePath = resolve(process.cwd(), genomeFile);
// ... execution begins
});
}Step 2: Genome Execution & Validation
Genome Execution via tsx
The CLI executes the TypeScript genome file using tsx:
// src/commands/new.ts
async function executeTypeScriptGenome(genomePath: string): Promise<Genome> {
// Read the genome file
const genomeCode = readFileSync(genomePath, 'utf-8');
// Execute with tsx runtime
const result = execSync(`tsx -e "${wrapperCode}"`, {
encoding: 'utf-8'
});
// Parse the returned Genome object
const genome = JSON.parse(result.trim());
return genome;
}Why tsx?: Allows users to write real TypeScript with imports, full IDE support, and type checking.
Genome Validation
// Validate genome structure
const validation = validateRecipe(genome);
if (!validation.valid) {
// Show errors and exit
validation.errors.forEach(error => logger.error(error));
process.exit(1);
}Validates:
- Project structure (name, path)
- Module format (id, category, parameters)
- Required fields present
Step 3: Orchestrator Initialization
// src/commands/new.ts
const projectManager = new ProjectManager(genome.project);
const orchestrator = new OrchestratorAgent(projectManager);
const result = await orchestrator.executeRecipe(genome);ProjectManager Creates:
- Project directory
- Initial package.json
- Project context object
OrchestratorAgent Receives:
- Validated genome
- Project context
- Execution options
Step 4: Module Resolution & Ordering
Dependency Graph Construction
// src/core/services/dependency/execution-planner.ts
const dependencyGraph = new DependencyGraph();
// Add modules as nodes
genome.modules.forEach(module => {
dependencyGraph.addNode(module.id);
});
// Add dependencies as edges
genome.modules.forEach(module => {
const blueprint = loadBlueprint(module.id);
blueprint.requires.forEach(prereq => {
dependencyGraph.addEdge(prereq, module.id);
});
});Topological Sort
// Sort modules by dependencies
const executionOrder = dependencyGraph.topologicalSort();
// Result: Adapters β Connectors β Features
// Example: ['framework/nextjs', 'database/drizzle', 'connector/drizzle-nextjs']Critical insight: This automatic ordering ensures adapters run before connectors that depend on them.
Step 5: Configuration Merging
For each module, merge defaults with user parameters:
// src/agents/orchestrator-agent.ts
const mergedConfig: MergedConfiguration = {
module: {
id: module.id,
version: module.version,
defaults: blueprint.defaults, // From blueprint
parameters: module.parameters // From user genome
},
project: projectContext,
activeFeatures: determineActiveFeatures(module)
};Why merge?: Blueprint defines defaults, user overrides only what they want to change.
Step 6: Blueprint Loading
// src/core/services/marketplace/blueprint-loader.ts
const blueprintPath = resolveModulePath(module.id, marketplace);
const blueprintModule = await import(blueprintPath);Loads from marketplace:
- Adapter blueprint:
marketplace/adapters/database/drizzle/blueprint.ts - Connector blueprint:
marketplace/connectors/drizzle-nextjs/blueprint.ts
Step 7: VFS Creation (Per-Blueprint)
// src/agents/orchestrator-agent.ts
// Create dedicated VFS for this blueprint
const vfs = new VirtualFileSystem(
`blueprint-${blueprint.id}`,
projectRoot
);Key design: One VFS per blueprint, not one global VFS.
Why:
- Blueprint isolation
- Independent rollback
- Pre-population per blueprint's needs
- Atomic per-blueprint commits
Step 8: Blueprint Preprocessing
// src/core/services/blueprint-preprocessor/blueprint-preprocessor.ts
const result = await preprocessor.processBlueprint(
blueprintModule,
mergedConfig
);
// If dynamic blueprint (function)
if (typeof blueprintModule.default === 'function') {
const actions = blueprintModule.default(mergedConfig);
return { actions };
}
// If static blueprint (object)
return { actions: blueprintModule.default.actions };Dynamic blueprint example:
// Blueprint as function
export default function(config: MergedConfiguration): BlueprintAction[] {
const actions = [];
// Always add core
actions.push({ type: 'CREATE_FILE', path: 'src/core.ts' });
// Conditionally add based on features
if (config.activeFeatures.includes('auth')) {
actions.push({ type: 'CREATE_FILE', path: 'src/auth.ts' });
}
return actions;
}Result: Static array of actions ready for execution.
Step 9: Blueprint Analysis
// src/core/services/project/blueprint-analyzer/blueprint-analyzer.ts
const analysis = blueprintAnalyzer.analyzeBlueprint(blueprint, context);
// Returns:
// {
// filesToRead: ['package.json', 'tsconfig.json', 'src/app/layout.tsx'],
// requiresVFS: true
// }Why analyze?: The executor needs to know which files to pre-load into VFS.
Scans for:
ENHANCE_FILEactions β adds their pathscontextualFilesproperty β adds listed files- Modifiers that need specific files
Step 10: VFS Pre-population
// src/core/services/execution/blueprint/blueprint-executor.ts
if (analysis.filesToRead.length > 0) {
await vfs.initializeWithFiles(analysis.filesToRead);
}Loads files from disk into VFS:
// VFS now contains:
// - package.json (in memory)
// - tsconfig.json (in memory)
// - src/app/layout.tsx (in memory)Why: ENHANCE_FILE actions can now find their target files in VFS.
Step 11: forEach Action Expansion
If blueprint has forEach actions, expand them:
// Blueprint action:
{
type: 'CREATE_FILE',
forEach: 'module.parameters.components', // ['button', 'card', 'input']
path: 'components/ui/{{item}}.tsx'
}
// Expands to:
[
{ type: 'CREATE_FILE', path: 'components/ui/button.tsx' },
{ type: 'CREATE_FILE', path: 'components/ui/card.tsx' },
{ type: 'CREATE_FILE', path: 'components/ui/input.tsx' }
]Step 12: Action Execution
// src/core/services/execution/blueprint/blueprint-executor.ts
for (const action of expandedActions) {
const result = await actionHandlerRegistry.handleAction(
action,
context,
projectRoot,
vfs // β All operations on VFS
);
if (!result.success) {
// FAIL FAST
return { success: false, errors: [result.error] };
}
}Action Handler Routing
// src/core/services/execution/blueprint/action-handlers/action-handler-registry.ts
async handleAction(action: BlueprintAction, context, root, vfs) {
switch (action.type) {
case 'CREATE_FILE':
return await createFileHandler.handle(action, context, root, vfs);
case 'ENHANCE_FILE':
return await enhanceFileHandler.handle(action, context, root, vfs);
case 'INSTALL_PACKAGES':
return await installPackagesHandler.handle(action, context, root, vfs);
// ... more handlers
}
}Step 13: Modifier Execution (for ENHANCE_FILE)
When action needs complex modification:
// Action:
{
type: 'ENHANCE_FILE',
path: 'src/app/layout.tsx',
modifier: 'jsx-children-wrapper',
params: {
wrapperComponent: 'QueryProvider',
importFrom: '@/lib/query-client'
}
}
// Executor routes to modifier:
const modifier = modifierRegistry.get('jsx-children-wrapper');
await modifier.execute(filePath, params, context, vfs);Inside Modifier (AST Manipulation)
// src/core/services/file-system/modifiers/jsx-children-wrapper.ts
async execute(filePath, params, context, vfs) {
// 1. Read file from VFS
const content = await vfs.readFile(filePath);
// 2. Parse with ts-morph (AST)
const project = new Project({ useInMemoryFileSystem: true });
const sourceFile = project.createSourceFile(filePath, content);
// 3. Add import
sourceFile.addImportDeclaration({
moduleSpecifier: params.importFrom,
namedImports: [params.wrapperComponent]
});
// 4. Find the children prop and wrap
const jsxElement = sourceFile.getDescendantsOfKind(SyntaxKind.JsxElement)[0];
// ... AST manipulation ...
// 5. Write back to VFS
await vfs.writeFile(filePath, sourceFile.getFullText());
}Why AST?: Can reliably find and wrap children props even in complex JSX.
Step 14: VFS Flush to Disk
After blueprint completes successfully:
// src/agents/orchestrator-agent.ts
const executionResult = await blueprintExecutor.executeBlueprint(
blueprint,
context,
vfs
);
if (executionResult.success) {
// Atomic flush to disk
await vfs.flushToDisk();
logger.success(`β
${blueprint.name} completed`);
} else {
// VFS discarded, no disk changes
logger.error(`β ${blueprint.name} failed`);
}Atomic operation: All changes or none. No partial states.
Step 15: Next Blueprint
Orchestrator repeats Steps 7-14 for each blueprint in execution order:
for (const module of executionOrder) {
const vfs = new VirtualFileSystem(...); // New VFS
const result = await executeBlueprint(...); // Execute
if (result.success) await vfs.flushToDisk(); // Commit
}Step 16: Final Project Setup
After all blueprints:
// Install dependencies
if (!options.skipInstall) {
execSync('npm install', { cwd: projectRoot });
}
// Run post-install scripts
executePostInstallScripts();
// Log success
logger.success('π Project created successfully!');Complete Flow Diagram
User Command
β
βββββββββββββββββββββββββββββββββββββββββ
β 1. Load Genome (tsx execution) β
β ββ Execute TypeScript β
β ββ Parse resulting object β
β ββ Validate structure β
βββββββββββββ¬ββββββββββββββββββββββββββββ
β
βββββββββββββΌββββββββββββββββββββββββββββ
β 2. Initialize Orchestrator β
β ββ Create ProjectManager β
β ββ Set up context β
β ββ Prepare for execution β
βββββββββββββ¬ββββββββββββββββββββββββββββ
β
βββββββββββββΌββββββββββββββββββββββββββββ
β 3. Resolve Dependencies β
β ββ Build dependency graph β
β ββ Topological sort β
β ββ Determine execution order β
βββββββββββββ¬ββββββββββββββββββββββββββββ
β
β βββββββββββββββββββββββββββ
βββ FOR EACH MODULE: β
βββββββββββ¬ββββββββββββββββ
β
βββββββββββββββββββββββββΌββββββββββββββββ
β 4. Load Blueprint from Marketplace β
β ββ Resolve module path β
β ββ Import blueprint.ts β
β ββ Load into memory β
βββββββββββββ¬ββββββββββββββββββββββββββββ
β
βββββββββββββΌββββββββββββββββββββββββββββ
β 5. Merge Configuration β
β ββ Blueprint defaults β
β ββ User parameters β
β ββ Active features β
βββββββββββββ¬ββββββββββββββββββββββββββββ
β
βββββββββββββΌββββββββββββββββββββββββββββ
β 6. Preprocess Blueprint β
β ββ Dynamic? Execute function β
β ββ Static? Extract actions β
β ββ Return action array β
βββββββββββββ¬ββββββββββββββββββββββββββββ
β
βββββββββββββΌββββββββββββββββββββββββββββ
β 7. Create Blueprint VFS β
β ββ New VFS instance β
β ββ Analyze blueprint β
β ββ Pre-populate with files β
βββββββββββββ¬ββββββββββββββββββββββββββββ
β
βββββββββββββΌββββββββββββββββββββββββββββ
β 8. Expand forEach Actions β
β ββ Find forEach actions β
β ββ Resolve item arrays β
β ββ Create individual actions β
βββββββββββββ¬ββββββββββββββββββββββββββββ
β
β βββββββββββββββββββββββββββ
βββ FOR EACH ACTION: β
βββββββββββ¬ββββββββββββββββ
β
βββββββββββββββββββββββββΌββββββββββββββββ
β 9. Route to Action Handler β
β ββ CREATE_FILE β CreateFileHandlerβ
β ββ ENHANCE_FILE β EnhanceHandler β
β ββ etc... β
βββββββββββββ¬ββββββββββββββββββββββββββββ
β
βββββββββββββΌββββββββββββββββββββββββββββ
β 10. Execute Action (on VFS) β
β ββ Simple? Direct VFS write β
β ββ Complex? Use modifier β
β ββ Modifier uses AST (ts-morph) β
βββββββββββββ¬ββββββββββββββββββββββββββββ
β
β βββββββββββββββββββββββββββ
βββ REPEAT FOR ALL ACTIONS β
βββββββββββ¬ββββββββββββββββ
β
βββββββββββββββββββββββββΌββββββββββββββββ
β 11. Flush VFS to Disk (Atomic) β
β ββ All succeeded? Commit β
β ββ Any failed? Discard VFS β
β ββ Move to next blueprint β
βββββββββββββ¬ββββββββββββββββββββββββββββ
β
β βββββββββββββββββββββββββββ
βββ REPEAT FOR ALL MODULES β
βββββββββββ¬ββββββββββββββββ
β
βββββββββββββββββββββββββΌββββββββββββββββ
β 12. Post-Generation β
β ββ npm install β
β ββ Run post-install scripts β
β ββ Log success message β
βββββββββββββββββββββββββββββββββββββββββ
β
PROJECT READYDetailed Example: drizzle-nextjs Connector
Let's trace one module through the entire flow.
User's Genome
export default defineGenome({
modules: [
{ id: 'framework/nextjs' }, // Adapter
{ id: 'database/drizzle' }, // Adapter
{ id: 'connector/drizzle-nextjs' } // Connector
]
});Execution Order (After Topological Sort)
1. framework/nextjs (no prerequisites)
2. database/drizzle (no prerequisites)
3. connector/drizzle-nextjs (requires: nextjs, drizzle)When drizzle-nextjs Executes:
1. VFS Created
const vfs = new VirtualFileSystem('blueprint-drizzle-nextjs', './my-app');2. Blueprint Loaded
// marketplace/connectors/drizzle-nextjs/blueprint.ts
export default function(config: MergedConfiguration): BlueprintAction[] {
return [
{
type: 'CREATE_FILE',
path: 'src/app/api/users/route.ts',
template: 'api-route.ejs',
data: { tableName: 'users' }
},
{
type: 'ENHANCE_FILE',
path: 'src/app/layout.tsx',
modifier: 'jsx-children-wrapper',
params: {
wrapperComponent: 'QueryProvider',
importFrom: '@/lib/query-client'
}
}
];
}3. Blueprint Analyzed
// Analyzer determines files needed:
analysis = {
filesToRead: ['src/app/layout.tsx'], // For ENHANCE_FILE
requiresVFS: true
};4. VFS Pre-populated
// Load src/app/layout.tsx into VFS (created by nextjs adapter)
await vfs.initializeWithFiles(['src/app/layout.tsx']);5. Actions Execute
Action 1: CREATE_FILE
// CREATE_FILE handler
await vfs.writeFile(
'src/app/api/users/route.ts',
renderedTemplate
);Action 2: ENHANCE_FILE
// ENHANCE_FILE handler β routes to modifier
const modifier = registry.get('jsx-children-wrapper');
// Modifier reads from VFS
const content = await vfs.readFile('src/app/layout.tsx');
// AST manipulation
// ... wraps children with QueryProvider
// Writes back to VFS
await vfs.writeFile('src/app/layout.tsx', modifiedContent);6. VFS Flushed
// Both files written to disk atomically
await vfs.flushToDisk();
// Disk now has:
// - src/app/api/users/route.ts (new)
// - src/app/layout.tsx (modified)Execution Characteristics
Sequential by Default
Blueprints execute sequentially in dependency order:
- Ensures prerequisites exist
- Predictable execution
- Simpler error handling
Trade-off: Could be parallelized for independent blueprints (future enhancement).
Fail-Fast Philosophy
First error stops execution:
if (!result.success) {
return { success: false, errors: [result.error] };
}Why: Prevents cascading failures. Better to stop early than create broken project.
Atomic Per-Blueprint
Each blueprint is an atomic unit:
- All actions succeed β VFS flushed
- Any action fails β VFS discarded
- No partial states
Performance Characteristics
Current Performance
Typical execution (SaaS Starter genome with 8 modules):
- Genome loading: ~50ms
- Dependency resolution: ~30ms
- Blueprint execution: ~2-5s total (depends on modules)
- npm install: ~15-30s (external)
VFS operations:
- Pre-population: ~10-50ms per blueprint
- Action execution: ~5-20ms per action
- AST operations: ~50-200ms per file
- Flush to disk: ~10-30ms per blueprint
Bottlenecks
- AST parsing (ts-morph) - Most expensive operation
- npm install - External process, can't optimize
- Sequential execution - Could parallelize
Optimizations
- β Lazy AST creation - Only parse when needed
- β Modifier caching - Reuse modifier instances
- β VFS in-memory - Faster than disk I/O
- β Pre-population - Batch file reads
Error Handling
Layer-by-Layer Error Handling
Each layer catches and wraps errors:
try {
const result = await nextLayer.execute();
return result;
} catch (error) {
throw new ArchitechError(
ArchitechErrorCode.LAYER_FAILURE,
`Layer X failed: ${error.message}`,
{ layer: 'X', originalError: error }
);
}Fail-Fast Propagation
Modifier fails
β
Action Handler fails
β
Executor fails
β
Orchestrator fails
β
Command exits with errorVFS discarded, no disk changes.
Next Steps
Dive deep into each layer:
Or explore specific systems:
Understanding the flow is the foundation. Now learn each component.