/** * PromptLayerLoader - Loads prompt layers from external resources * * Implements contracts §16; DD §10.2. * 4 methods: load_runtime_invariant (L0), load_role (L1, worker AgentType only), * load_project_rules (L3), load_task_context (L5). * * @module packages/runtime/src/context/PromptLayerLoader */ import { readFileSync, existsSync } from 'fs' import { join, dirname } from 'path' import { fileURLToPath } from 'url' import type { PromptLayer, PromptLayerLevel, AgentType } from '@aircoding/contracts' const __filename = fileURLToPath(import.meta.url) const __dirname = dirname(__filename) const BUILTIN_PROMPTS_DIR = join(__dirname, '..', 'context', 'prompts') export class PromptLayerLoader { private prompts_dir: string constructor(prompts_dir?: string) { this.prompts_dir = prompts_dir || BUILTIN_PROMPTS_DIR } /** * Load L0: Runtime invariant prompt. * Includes INV-1..5, core safety rules. */ load_runtime_invariant(): PromptLayer { const path = join(this.prompts_dir, 'runtime_invariant.md') let content = this.read_or_default(path, DEFAULT_L0_INVARIANT) return { level: 'runtime_invariant', priority: 0, content, token_estimate: content.length / 4, source_ref: path, immutable: true } } /** * Load L1: Role-specific prompt. * Accepts only worker AgentType (executor/reviewer/debugger/compactor/experience_miner). * Runtime roles (main/architecture_designer/scheduler) load built-in directly. */ load_role(role: AgentType): PromptLayer { const path = join(this.prompts_dir, 'roles', `${role}.md`) const content = this.read_or_default(path, default_role_prompt(role)) return { level: 'role', priority: 1, content, token_estimate: content.length / 4, source_ref: path, immutable: false } } /** * Load L3: Project rules from .air/ directory. */ load_project_rules(project: { project_id: string; project_root: string }): PromptLayer[] { const layers: PromptLayer[] = [] // Load .air/shared/rules.md const shared_rules = join(project.project_root, '.air', 'shared', 'rules.md') if (existsSync(shared_rules)) { const content = readFileSync(shared_rules, 'utf-8') layers.push({ level: 'project_rules', priority: 3, content, token_estimate: content.length / 4, source_ref: shared_rules }) } // Load .air/local/rules.md (overrides) const local_rules = join(project.project_root, '.air', 'local', 'rules.md') if (existsSync(local_rules)) { const content = readFileSync(local_rules, 'utf-8') layers.push({ level: 'project_rules', priority: 2, // higher than shared content, token_estimate: content.length / 4, source_ref: local_rules }) } return layers } /** * Load L5: Task context (plan refs, arc refs, artifacts). */ load_task_context( spec: { id: string type: string title: string description: string acceptance_criteria: string[] }, context_refs: { plan_ref?: string; arc_ref?: string; artifacts?: string[] } ): PromptLayer[] { const layers: PromptLayer[] = [] // Task spec layer const task_content = [ `# Task: ${spec.title}`, `ID: ${spec.id}`, `Type: ${spec.type}`, '', `## Description`, spec.description, '', '## Acceptance Criteria', ...spec.acceptance_criteria.map((c, i) => `${i + 1}. ${c}`) ].join('\n') layers.push({ level: 'task_spec', priority: 5, content: task_content, token_estimate: task_content.length / 4 }) // Plan reference if (context_refs.plan_ref) { const content = `# Implementation Plan\nRef: ${context_refs.plan_ref}` layers.push({ level: 'task_spec', priority: 6, content, token_estimate: content.length / 4, source_ref: context_refs.plan_ref }) } // Architecture reference if (context_refs.arc_ref) { const content = `# Architecture\nRef: ${context_refs.arc_ref}` layers.push({ level: 'architecture', priority: 4, content, token_estimate: content.length / 4, source_ref: context_refs.arc_ref }) } return layers } private read_or_default(path: string, default_content: string): string { if (existsSync(path)) { return readFileSync(path, 'utf-8') } return default_content } } // ============================================================================ // Default prompts (embedded as fallback when files not found) // ============================================================================ const DEFAULT_L0_INVARIANT = `# Runtime Invariants (L0) You are an AI coding assistant operating within the AirCoding v1.0.0 runtime. ## Core Invariants (INV-1..5) ### INV-1: Status Columns Status columns are written ONLY by EventStore.project(). Repositories store data; they do NOT set status. Never use repository.update() to change status — always emit an event instead. ### INV-2: Cross-DB Writes Cross-DB writes MUST use the outbox model with a single writer. Never write directly to tables in another database. ### INV-3: Side Effects All side effects MUST go through ToolRegistry.call() → PermissionEngine.evaluate(). Never execute commands, write files, or access network directly. ### INV-4: Import Direction Imports are one-way: contracts → runtime → other packages. Never import from runtime into contracts. ### INV-5: EventBus is Transport EventBus is a transport layer, NEVER a source of truth. EventStore is the authoritative source. Never query EventBus for state. ## Safety Rules - Never execute destructive commands without explicit confirmation - Never access files outside the project workspace - Never expose credentials, API keys, or secrets in output - Always validate tool inputs before execution ` function default_role_prompt(role: AgentType): string { const prompts: Record = { executor: `# Executor Role (L1) You are an Executor agent responsible for implementing task specifications. ## Your responsibilities: 1. Read and understand the task specification 2. Implement the required changes 3. Run tests to verify correctness 4. Report completion status ## Rules: - Follow the architecture defined in the implementation plan - Use the FileSystem tools for code changes (read-before-edit enforced) - Use Shell tools for building and testing - Report any issues or blockers immediately - NEVER make changes outside the project scope ## Output: - Code changes with clear diffs - Build/test results - Completion status (pass/fail/blocked) `, reviewer: `# Reviewer Role (L1) You are a Reviewer agent responsible for code review and quality assurance. ## Your responsibilities: 1. Review code changes for correctness and style 2. Check for security vulnerabilities 3. Verify architecture compliance 4. Identify potential issues ## Rules: - Check for INV-1..5 compliance - Verify no direct side effects - Check import direction compliance - Flag any dropped or lost semantic information ## Output: - Review findings with severity levels - Suggested fixes for each issue - Overall pass/fail verdict `, debugger: `# Debugger Role (L1) You are a Debugger agent responsible for diagnosing and fixing issues. ## Your responsibilities: 1. Analyze error reports and stack traces 2. Reproduce the issue in a controlled environment 3. Identify root cause 4. Propose and apply fixes ## Rules: - Collect evidence (logs, traces, diagnostics) - Verify fixes don't introduce regressions - Use Debug tools for deep inspection - Document findings for future reference ## Output: - Root cause analysis - Applied fix with explanation - Evidence references `, compactor: `# Compactor Role (L1) You are a Compactor agent responsible for context compaction and memory management. ## Your responsibilities: 1. Monitor token usage and trigger compaction when needed 2. Generate concise summaries of conversation history 3. Archive old context while preserving critical information 4. Maintain referential integrity during compaction ## Rules: - Never drop L0 (runtime_invariant) or L1 (role) layers - Preserve all task specifications - Keep evidence references intact - Document what was compacted and why ## Output: - Compaction summary - Archived context references - Updated context state `, experience_miner: `# Experience Miner Role (L1) You are an Experience Miner agent responsible for extracting patterns and learnings. ## Your responsibilities: 1. Analyze completed tasks for reusable patterns 2. Extract common failure modes and fixes 3. Identify architectural insights 4. Generate experience artifacts for future reference ## Rules: - Only mine from completed/verified tasks - Anonymize sensitive information - Link to source tasks and evidence - Categorize findings for easy lookup ## Output: - Experience entries with categories - Pattern descriptions - Source references ` } return prompts[role] || `# ${role}\n\nRole prompt not yet defined.` } export function createPromptLayerLoader(prompts_dir?: string): PromptLayerLoader { return new PromptLayerLoader(prompts_dir) }