Repository navigation
Prompt node type with PromptAssembler service and graph-override layering #1049
Description
Overview
Introduce a prompt node type that stores prompt text as first-class graph nodes, and a PromptAssembler service that composes the final agent prompt from hardcoded defaults + graph-stored overrides. This is Phase 2 of ADR-030.
Problem Statement
After #1040 improves the hardcoded prompts, they remain hardcoded in Rust. Every prompt adjustment requires a code change and rebuild. Users cannot customize agent behavior. There is no versioning, no rollback, and no semantic relationship between prompts and the knowledge they operate on.
Proposed Solution
Store prompts as nodes in the knowledge graph with a purpose-built assembler service that layers graph overrides on top of hardcoded defaults.
New node type: prompt
Properties:
- priority: integer (assembly ordering)
- template_syntax: "plain" | "minijinja"
- source: "built-in" | "user-modified" | "user-created"
Content: the prompt text or template
Role (system, few-shot, tool-definition, etc.) is encoded on the relationship edge, not as a node property:
agent-config -[includes {role: "system", order: 1}]-> prompt-node
PromptAssembler service
Purpose-built service in packages/agent/ (~200-300 lines):
impl PromptAssembler {
async fn assemble(
&self,
agent_config_id: &str,
context: &TemplateContext,
) -> Result<AssembledPrompt>;
}Assembly order:
- Load hardcoded base prompt (always present)
- Fetch prompt nodes connected to agent-config, ordered by priority
- Render Minijinja templates with context variables
- Concatenate in priority order
Layering model
Hardcoded defaults are always the foundation. Graph-stored prompts override and extend, never replace.
- Cold start: empty DB works with hardcoded defaults
- Corruption recovery: "reset to defaults" restores built-in prompts
- Upgrade path: new hardcoded defaults ship with the app; user customizations layer on top
Caching
HashMap-based cache keyed by agent-config ID. Invalidate on node update events. Lazy rebuild on next access.
Acceptance Criteria
-
promptnode type registered with behavior and schema -
promptnodes get embedded (searchable via semantic search) -
PromptAssemblerservice implemented inpackages/agent/ - Hardcoded base + graph overrides layering works correctly
- Minijinja integration for
{{ variable }}substitution in prompt content - Seed default prompt nodes created on first run (migrate content from
prompt_templates.rs) - Cache with event-driven invalidation implemented
- Built-in prompts can be upgraded without overwriting user modifications
- "Reset to defaults" functionality for prompt nodes
- Content boundary markers for user-authored content in assembled prompts
- Tests: assembler ordering, override layering, cache invalidation, template rendering
- Code passes
bun run quality:fix - Tests pass (
bun run test:all)
Technical Specifications
Reference Files (post-#1040)
- PromptAssembler location:
packages/agent/src/prompt_assembler.rs(new) - Prompt behavior:
packages/core/src/behaviors/mod.rs(extend with prompt type) - Prompt schema:
packages/core/src/schemas/(new prompt schema) - Hardcoded base:
packages/agent/src/local_agent/prompt_templates.rs(keep, becomes base layer) - Agent loop integration:
packages/agent/src/local_agent/agent_loop.rs(call assembler instead of raw prompt_templates) - Cache invalidation: Subscribe to node update events from NodeService event channel
Minijinja Integration
- Add
minijinjacrate dependency topackages/agent/Cargo.toml - Template context includes: current_date, model_name, workspace_context (from context_ops)
- Template errors degrade gracefully to hardcoded base (never crash the turn)
Prompt Node Schema
{
"node_type": "prompt",
"properties": {
"priority": { "type": "integer", "default": 100 },
"template_syntax": { "type": "string", "enum": ["plain", "minijinja"], "default": "plain" },
"source": { "type": "string", "enum": ["built-in", "user-modified", "user-created"], "default": "user-created" }
}
}ADR Reference
Dependencies
- Refine local agent system prompt for better tool usage and response quality #1040 (prerequisite — creates
packages/agent/crate, ops layer, and refined hardcoded prompts)
Non-Goals
- Skill nodes (separate issue, Phase 3)
- Semantic skill discovery (separate issue, Phase 4)
- Tool whitelist scoping (part of skill nodes)
find_skillstool (Phase 4)
Activity
malibio commentedon Apr 6, 2026
Frontend baseline: 128 test files, 3918 tests all passing (34.78s)
Add prompt node type with PromptAssembler service and graph-override …
Prompt node type with PromptAssembler service and graph-override laye…
Prompt node type with PromptAssembler service and graph-override laye…