Orchestration 2 Plan 1
Scope: Implement the include directive system in agent-sync.ts and create the shared snippet files that agent prompts will reference in Plan 2.
Task 1 — Implement include directive in agent-sync.ts
Spec §1: Agent prompt files support <!-- include: path --> directives resolved at agent-sync time. Spec §2: Resolution is recursive with cycle detection and max depth.
The readPromptFile() function in /workspace/erik/uvilo-os/orchestrator/src/agent-sync.ts currently reads a file, strips frontmatter, and returns content. Add include resolution after frontmatter stripping.
Current readPromptFile() (replace entirely):
function readPromptFile(promptFile: string): string | null {
const fullPath = resolve(REPO_ROOT, promptFile);
if (!existsSync(fullPath)) {
console.log(`WARNING: Prompt file not found at ${fullPath}`);
return null;
}
return stripFrontmatter(readFileSync(fullPath, 'utf-8'));
}
Replace with:
const MAX_INCLUDE_DEPTH = 10;
const INCLUDE_REGEX = /<!-- include: (.+?) -->/g;
function resolveIncludes(content: string, depth: number, visited: Set<string>): string {
if (depth <= 0) {
throw new Error(`Include depth exceeded (max ${MAX_INCLUDE_DEPTH}). Check for deep nesting.`);
}
return content.replace(INCLUDE_REGEX, (_match, includePath: string) => {
const trimmedPath = includePath.trim();
const fullPath = resolve(REPO_ROOT, trimmedPath);
// Cycle detection
if (visited.has(trimmedPath)) {
throw new Error(`Include cycle detected: ${trimmedPath} (visited: ${[...visited].join(' → ')})`);
}
// Missing file check
if (!existsSync(fullPath)) {
throw new Error(`Include file not found: ${trimmedPath} (resolved to ${fullPath})`);
}
const included = stripFrontmatter(readFileSync(fullPath, 'utf-8'));
const newVisited = new Set(visited);
newVisited.add(trimmedPath);
return resolveIncludes(included, depth - 1, newVisited);
});
}
function readPromptFile(promptFile: string): string | null {
const fullPath = resolve(REPO_ROOT, promptFile);
if (!existsSync(fullPath)) {
console.log(`WARNING: Prompt file not found at ${fullPath}`);
return null;
}
const content = stripFrontmatter(readFileSync(fullPath, 'utf-8'));
return resolveIncludes(content, MAX_INCLUDE_DEPTH, new Set([promptFile]));
}
Key details:
MAX_INCLUDE_DEPTH = 10 — hard limit on nesting
INCLUDE_REGEX matches <!-- include: path --> (path is relative to repo root)
resolveIncludes() is recursive — each included file is itself resolved for includes
- Cycle detection uses a
visited Set of paths; encountering a previously seen path throws
- Missing files throw (fail loudly, not silently)
stripFrontmatter() already exists in the file — reuse it for each included file
- The starting
visited set includes the prompt file itself to prevent self-inclusion
- No new npm dependencies needed
Testing: After implementation, run:
npx tsx /workspace/erik/uvilo-os/orchestrator/src/agent-sync.ts --dry-run
This should produce the same output as before (no includes exist yet). No errors should occur.
Task 2 — Create shared environment snippet
Spec §3.1: Forge/Configs/Agents/Env_Snippet.md — environment facts and universal rules. Included by all agents except Forge Chat.
Create the file Forge/Configs/Agents/Env_Snippet.md with this exact content:
---
title: "Env Snippet"
visibility: internal
status: published
owner: "erik@uvilo.com"
---
## Environment
- Git: ErikDakoda/uvilo-os (private), canonical branch `dev`
- Don't merge into `main` unless explicitly asked
- Railway cloud — no local filesystem; shell via forge-bash only
- Deferred tools: most MCP tools are deferred; use ToolSearch to load on demand
## Universal Rules
- No `rm` — always use uvilo-trash for deletion
- For sidebar/URL concerns, spawn Page Handler instead of handling directly
- Diagnosis rule: verify most direct cause first; don't re-derive from scratch; search the internet for existing solutions
- Escalation rule: after 3 failed infrastructure attempts, stop and ask the user
This file is not a standalone prompt — it is a snippet included by other prompt files via <!-- include: Forge/Configs/Agents/Env_Snippet.md -->.
Task 3 — Create shared project snippet
Spec §3.2: Forge/Configs/Agents/Project_Snippet.md — project lifecycle mental model. Included by Project Worker, Project Thinker, Project Evaluator.
Create the file Forge/Configs/Agents/Project_Snippet.md with this exact content:
---
title: "Project Snippet"
visibility: internal
status: published
owner: "erik@uvilo.com"
---
### Project Lifecycle
Create → Requirements → Research → Spec → Plan → Implement → Evaluate → Fix → Verify → Extract → Complete
Projects have one or more Plans (Plan 1, Plan 2, etc). The Implement → Evaluate → Fix loop repeats within a plan until Evaluate passes, and then loops over the next Plan.
When Evaluate passes for all Plans → Verify project.
If Verify finds issues → new Plan → Implement → Evaluate → Fix loop. If Verify passes → Extract.
This file is included by Project Worker, Project Thinker, and Project Evaluator via <!-- include: Forge/Configs/Agents/Project_Snippet.md -->. NOT included by Project Runner, Task Runner, Page Handler, or Forge Chat.
Task 4 — Verify and commit
- Run agent-sync dry-run to confirm no errors:
npx tsx /workspace/erik/uvilo-os/orchestrator/src/agent-sync.ts --dry-run
- Run agent-sync live to verify assembled prompts in production:
npx tsx /workspace/erik/uvilo-os/orchestrator/src/agent-sync.ts
- Add sidebar entries for the two new snippet files (follow Update_Sidebar skill)
- Build to verify:
cd /workspace/erik/uvilo-os/.internal && pnpm run build
- Stage all changes, commit with message
Orchestration_2: Plan 1 — include directive + shared snippets, push to dev