Skip to content
approved Visibility internal Owner erik@uvilo.com Approver _ Created 2026-07-25 Updated 2026-07-25

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

  1. Run agent-sync dry-run to confirm no errors:
npx tsx /workspace/erik/uvilo-os/orchestrator/src/agent-sync.ts --dry-run
  1. Run agent-sync live to verify assembled prompts in production:
npx tsx /workspace/erik/uvilo-os/orchestrator/src/agent-sync.ts
  1. Add sidebar entries for the two new snippet files (follow Update_Sidebar skill)
  2. Build to verify: cd /workspace/erik/uvilo-os/.internal && pnpm run build
  3. Stage all changes, commit with message Orchestration_2: Plan 1 — include directive + shared snippets, push to dev