Rules Architecture - Z-M-Huang/openhive GitHub Wiki
Rules Architecture
Rules are the primary mechanism for defining and evolving agent behavior in OpenHive. Code enforces invariants (workspace boundaries, secret scoping, communication paths). Rules define everything else: what agents do, how they make decisions, what policies they follow, and how they improve over time.
Core principle: If behavior can be expressed as a rule, it is a rule -- not code.
Table of Contents
Sub-pages:
- Skills -- Modular, reusable procedures (HOW to perform tasks)
- Skill-Repository -- Online skill discovery via Vercel skills ecosystem
- Subagents -- Agent identity definitions (WHO does the work)
- Self-Evolution -- Rule governance, modification permissions, and self-improvement protocol
Rule Philosophy
Rules exist because code is expensive to change and easy to get wrong. A markdown rule can be edited, reviewed, and deployed in minutes. A code change requires testing, building, and restarting the container.
The division of responsibility:
| Layer | Responsibility | Examples |
|---|---|---|
| Rules (markdown) | Behavior, policy, judgment, procedures | "Escalate incidents within 5 minutes", "Review all PRs before merge", "Use deploy skill for releases" |
| Code (TypeScript) | Invariants, enforcement, I/O | Workspace boundary guards (tool-guards.ts), secret resolution, channel adapters, organization tool validation |
Rules are loaded into agent sessions as part of the systemPrompt. They are the agent's instructions -- the agent follows them because they are part of its prompt, not because code forces compliance. Code-enforced invariants exist as a safety net for properties that must never be violated regardless of what rules say (e.g., a team cannot access another team's secrets even if a rule tells it to).
When to Use Rules vs Code
Use a rule when:
- The behavior should be easy to change without redeployment
- Different teams may need different versions of the behavior
- The behavior requires judgment, not mechanical enforcement
- You want agents to be able to propose improvements
Use code when:
- Violation would compromise security or data integrity
- The check must be mechanically enforced (no judgment)
- The behavior is structural (session lifecycle, message routing)
- Failure must result in a hard block, not a soft guideline
Rule Types
System Rules
Location: baked into the container image
Apply to every agent in the system unconditionally. These rules are compiled into the image at build time and cannot be changed without a new image build. They serve two purposes: (1) code-enforced invariants (workspace boundaries, secret handling, communication path enforcement) that agents cannot bypass, and (2) behavioral guidance (tool selection framework, task workflow) loaded into the systemPrompt via cascade.ts so agents follow them as highest-priority instructions.
Agents cannot modify or override system rules. The behavioral guidance rules are loaded into the prompt at cascade Tier 1 (highest precedence). The code-enforced invariants operate independently of the prompt.
System rules ship baked into the container image at /app/system-rules/. They include: core agent interaction patterns, main agent identity and orchestration instructions, SDK capabilities reference, sender trust framework, structured task workflow, activation-mode selection (see Tool-Guidelines#Activation Decision Framework — ADR-44), tool selection guidelines, and system-level skills (learning and reflection cycles). See Architecture#Data Layout for the full directory structure.
Tier 1 distribution of the Activation Decision Framework. Because the framework teaches orchestrators when work is on-demand vs trigger-scheduled vs continuous-watch, it MUST reach every team uniformly. Placing it at Tier 1 (system rules) means every existing team (main, and every child team) and every future team inherits it automatically — no per-team YAML edits required. Reflection cycles (Self-Evolution#Self-Reflection) flag repeated on-demand use of patterns that should be trigger-driven.
Admin Org Rules
Location: /data/rules/*.md
Apply to every agent in the system. All .md files in the directory are loaded. These define organization-wide policies that no team can override at runtime.
Admin org-rules are written by human administrators and stored in the config volume (/data/). Agents cannot modify them without admin approval. This directory starts empty -- no files are seeded at install time. Administrators place custom org-rules here as needed.
Admin org-rules live in /data/rules/. This directory starts empty -- administrators place custom .md files here as needed (e.g., ethics constraints, escalation policies). All files apply to every agent.
Ancestor Org Rules
Location: .run/teams/{name}/org-rules/*.md
Cascade to the current team AND all sub-teams at any depth. All .md files in the directory are loaded. A team's org-rules are inherited by every descendant in its subtree.
Teams are runtime entities stored in the runtime workspace volume (.run/). Use ancestor org-rules for policies that a parent team wants to impose on its entire branch of the hierarchy.
Ancestor org-rules live in .run/teams/{name}/org-rules/. For example, an engineering team might maintain code-standards.md and testing-policy.md here -- all engineering sub-teams inherit these automatically.
Team Rules
Location: .run/teams/{name}/team-rules/*.md
Apply only to the current team. All .md files in the directory are loaded. These do not cascade to children.
Use team-rules for behavior specific to one team that should not affect its sub-teams.
Team-only rules live in .run/teams/{name}/team-rules/. For example, a frontend team might maintain component-patterns.md and accessibility.md -- these apply only to the frontend team and do not cascade to its children.
Rule Cascade
The rule cascade is assembled per-message in message-handler.ts via cascade.ts by walking the hierarchy from the root to the target team. Rules are concatenated in order, with later rules appearing after earlier ones in the prompt.
Loading Order
Rules are loaded in this order (later rules appear after earlier ones in the prompt):
- System rules (baked into image, enforced by code)
- Admin org-rules from
/data/rules/*.md(config volume) - Each ancestor's org-rules from root to parent
- Team's own org-rules (cascades to descendants)
- Team's own team-rules (no cascade)
Example: Frontend Team
The frontend team (child of engineering, grandchild of main) receives rules in this order: system rules (baked in), then any admin org-rules from /data/rules/, then engineering's org-rules (e.g., code-standards, testing-policy), then frontend's own org-rules (e.g., ui-standards), and finally frontend's team-rules (e.g., component-patterns, accessibility).
Additional Loading Strategies
The cascade above covers what is loaded into systemPrompt at spawn. Agents have additional ways to access rules during execution:
| Source | What It Provides | When Loaded |
|---|---|---|
systemPrompt (cascade) |
Core rules from the full hierarchy | Session start |
Skills (skills/*.md) |
Step-by-step procedures loaded into prompt | Session start |
Memory (SQLite memories table) |
Persistent team context, lessons, decisions | Session start |
| Direct file reads | One-off rules or situational overrides | As needed |
For large rule sets, keep the systemPrompt cascade focused on essential policies and load detailed reference material through direct file reads on demand.
Prompt Cache Boundary
The rule cascade is divided into a static prefix and a dynamic suffix to maximize prompt cache hit rates (ADR-23).
| Segment | Contents | Cache Behavior |
|---|---|---|
| Static prefix | System rules + admin org-rules (/data/rules/*.md) + tool usage guide + HTTP rules | Identical across all teams and sessions. Cached once, reused globally. |
| Dynamic suffix | Core instructions (cwd), tool availability, ancestor org-rules, team org-rules, team-rules, skills, memory, conversation history | Varies per team and per message. Not cached. Credentials are not in prompts — accessed at runtime via vault_get. |
The boundary is the transition from admin org-rules to ancestor org-rules. Everything before is shared; everything after is team-specific.
Rule File Format
Rule files are markdown. There is no required schema -- the agent reads them as natural language instructions. However, following consistent conventions makes rules easier to write, review, and evolve.
Conventions
- One concern per file. A rule file should address a single topic (e.g., "code review process", "escalation policy", "deploy procedure").
- Start with a clear heading. The first
#heading names the rule. - State the purpose. A short paragraph explaining what this rule governs and why it exists.
- Be specific. "Respond within 5 minutes" is better than "respond quickly."
- Use imperative mood. "Escalate to parent when..." not "The agent should consider escalating..."
- Include examples where helpful. Show the agent what correct behavior looks like.
- Avoid contradicting higher-level rules. Team-rules should not conflict with org-rules or global rules. If a conflict exists, higher-level rules take precedence (global > org > team).
Example Rule Patterns
Global ethics rule -- Defines non-overridable constraints: no harmful content generation, no out-of-scope access, no fabrication, no plain-text secret handling, respect for user privacy. Includes an escalation fallback: when in doubt, escalate to parent.
Team orchestrator rule -- Defines routing behavior for a parent team: always delegate tasks to matching subagents (ADR-40), delegate to child teams when appropriate, escalate out-of-scope work. Includes task delivery standards (acceptance criteria, status updates) and escalation triggers.
Team-specific rule -- Defines conventions for a single team (e.g., component patterns, state management guidelines, accessibility requirements) that do not cascade to children.
Rule Validation
Rule validation runs at message time -- when message-handler.ts assembles the systemPrompt for a team session via cascade.ts. Before concatenating the rule cascade, the validator checks for conflicts.
What constitutes a conflict: Two rules at different cascade levels address the same topic with contradictory directives. For example, a global rule says "never deploy on Fridays" and a team-rule says "deploy any day of the week."
Precedence order (highest to lowest): system rules > admin org-rules > ancestor org-rules > team-rules.
Higher-level rules always take precedence. Specifically:
- Soft override: If a lower-level rule refines or narrows a higher-level rule without contradicting it, both are loaded normally (e.g., admin org-rules say "test before deploy", team-rule adds "run integration tests AND unit tests").
- Hard conflict: If a team-rule directly contradicts an admin org-rule or an inherited ancestor org-rule, the validator logs a warning identifying both rules and the conflict (validator.ts:34). The session still starts — current implementation warns rather than blocks. Future iterations may enforce hard blocks for critical conflicts.
- Explicit override with
[OVERRIDE]prefix: A rule can include[OVERRIDE]in its heading to explicitly override a parent rule on the same topic. This requires admin approval and is logged. For example, a hotfix team might override a Friday deploy freeze.
Validation steps at spawn:
- Collect all rules in cascade order (system rules -> admin org-rules -> ancestor org-rules -> team-rules)
- For each rule, extract its topic (from the
#heading) - Detect same-topic rules at different levels
- If contradictory and no
[OVERRIDE]marker: log warning with both rules identified (current behavior; future: may block spawn) - If
[OVERRIDE]is present: verify admin approval flag exists, then allow the override - Log all detected overlaps (even non-conflicting ones) at
debuglevel for audit
For details on who can modify rules and the governance process, see Self-Evolution.
Quick Reference
File Locations
| Purpose | Path | Volume |
|---|---|---|
| System rules | baked into image | image |
| Sender trust rules | /app/system-rules/sender-trust.md (baked into image) |
image |
| Admin org-rules | /data/rules/*.md |
config volume |
| Ancestor org-rules | .run/teams/{name}/org-rules/*.md |
runtime workspace |
| Team-only rules | .run/teams/{name}/team-rules/*.md |
runtime workspace |
| Skills | .run/teams/{name}/skills/*.md |
runtime workspace |
| Subagent definitions | .run/teams/{name}/subagents/*.md |
runtime workspace |
| Team config | .run/teams/{name}/config.yaml |
runtime workspace |
| Team memory | SQLite memories table (per-team isolation) |
runtime workspace |
Cascade Summary
| Rule Level | Applies To |
|---|---|
| System rules (baked in) | All agents (code-enforced, cannot be overridden) |
Admin org-rules (/data/) |
All agents (admin-managed config volume) |
Ancestor org-rules (.run/) |
Team + all descendants |
Team-rules (.run/) |
That team only |
Governance Summary
| Rule Level | Modifiable By |
|---|---|
| System rules | Image rebuild only |
| Admin org-rules | Admin only |
| Org-rules | Owning team (logged, affects descendants) |
| Team-rules | Owning team (logged, team-scoped) |
| Skills | Owning team (logged) |
| Subagents | Owning team (logged) |