Design decisions and rationale for agent-homebase.
agent-homebase is a library of agent skills, instructions, and supporting infrastructure for VS Code Copilot agent mode and Claude Code. It provides:
- 13 specialized skills for software delivery workflows (canonical source of truth)
- 13 agent wrappers for VS Code (generated from skills with tool restrictions and subagent delegation)
- 25 instruction files for governance and contracts
- 4-phase implementation for production-grade execution
Skills are the single source of truth. When editor.target includes "vscode", init.py generates thin .agent.md wrappers that provide native tool restrictions, context isolation, and model selection. Claude Code users consume skills directly.
skills/ ← canonical source (both platforms)
architect/architect.skill.md
qa/qa.skill.md
...
resolved/ ← generated output
skills/ ← always generated (resolved as SKILL.md per VS Code convention)
agents/ ← generated only when editor.target includes "vscode"
instructions/ ← always generated
Best pattern: Agent delegates to skill. Agent provides persona + tool boundary. Skill provides procedural knowledge + bundled assets.
Orchestrators (@sprint-lead) delegate ALL heavy work to subagents:
@sprint-lead (thin)
├── Reads plans
├── Tracks state
├── Collects summaries
└── Delegates to:
├── Unnamed subagents (implementation)
├── @qa (quality gates)
├── @security (vulnerability audit)
├── @reviewer (code review)
└── @docs (documentation)
Why? Context limits. An orchestrator that reads source files and runs commands will exhaust its context window. Delegation keeps each agent focused.
All agent interactions follow defined contracts:
- Return schemas: Tier 1/2/3 JSON schemas define what agents return
- Write permits: Agents can only write to permitted paths
- Severity levels: Findings use consistent CRITICAL/WARNING/SUGGESTION
Why? Predictability. When @qa returns status: blocked, @sprint-lead knows exactly how to handle it.
Behavior is controlled through project.config.yml, not code changes:
quality:
coverage_threshold: 85 # Change threshold without editing skills
commands:
test: "pnpm test" # Use project's actual commandsWhy? Portability. The same skills work across different projects with different toolchains.
Four phases add capabilities incrementally:
| Phase | Capability | Required |
|---|---|---|
| 0 | Security validation, observability | ✅ |
| 1 | Formal contracts, policy engine | ✅ |
| 2 | Durable execution, checkpoints | ○ |
| 3 | Sandboxed execution | ○ |
| 4 | Deterministic replay | ○ |
Why? Not every project needs full durability. Start simple, add phases as needed.
Phase 0 (Security): Always required. Protects against dangerous commands and path traversal.
Phase 1 (Contracts): Always required. Ensures agents return predictable data for orchestration.
Phase 2 (Durability): Use for sprints longer than 4 hours or when interruptions are likely. Enables checkpoint/resume.
- Skip if: Short sprints (<1 hour), disposable work
- Use if: Multi-day sprints, unstable network, need audit trail
Phase 3 (Sandboxing): Use when running untrusted code or limiting resource usage.
- Skip if: Trusted codebase, local development only
- Use if: CI/CD pipelines, unknown dependencies, resource quotas required
Phase 4 (Determinism): Use when reproducibility is critical (debugging, audits, compliance).
- Skip if: Results don't need to be bit-for-bit identical
- Use if: Regulatory requirements, troubleshooting non-deterministic failures
Problem: Agent systems can execute dangerous commands, leak secrets, or produce unpredictable output.
Solution: Security validation in init.py:
- Command whitelist (no
rm -rf) - Path traversal detection (no
../../../etc/passwd) - Secret scanning (no API keys in config)
Problem: Agents return inconsistent data, making orchestration brittle.
Solution: JSON Schema validation + Rego policies:
- Return schemas enforce structure
- Policies enforce business rules (feature/bug balance, capacity)
- FSM ensures valid state transitions
Problem: Long-running sprints can fail mid-execution, losing progress.
Solution: SQLite persistence + checkpoints:
- State snapshots at phase boundaries
- Resume from any checkpoint
- Bidirectional markdown ↔ SQLite migration
Problem: Agent tasks (tests, builds) can affect host system or access unauthorized resources.
Solution: Docker container isolation:
- Resource limits (CPU, memory, time)
- Network policies (deny by default)
- Capability framework (fine-grained permissions)
Problem: Replaying a sprint produces different results due to time, randomness, composition order.
Solution: Deterministic execution:
- Lamport timestamps (logical time)
- Prompt versioning (detect skill changes)
- Content-based tie-breaking (reproducible ordering)
- LLM config enforcement (temperature=0)
| Option | Pros | Cons |
|---|---|---|
| Hardcoded Python | Simple | Not configurable, hard to audit |
| YAML rules | Readable | Limited expressiveness |
| JSON Schema | Standard | Only validates structure, not logic |
| Rego | Powerful, auditable, standard | Learning curve |
Rego (Open Policy Agent) provides:
- Declarative rules: Easy to read and audit
- Composition: Policies can reference each other
- Testing: Built-in test framework
- Ecosystem: Industry standard for policy-as-code
Example policy:
# Violation if feature allocation exceeds cap
violation[msg] {
input.constraints.featurePercent > 70
msg := sprintf("Feature allocation %v%% exceeds 70%% cap", [input.constraints.featurePercent])
}Returns that produce no artifacts. Used for validation, recommendations.
{
"tier": 1,
"status": "complete",
"summary": "Analysis complete",
"findings": [...]
}Use cases: @pm validation, @qa pipeline, @reviewer feedback
Returns that produce a single artifact (document, code file).
{
"tier": 2,
"status": "complete",
"summary": "Draft created",
"artifactPath": "docs/draft.md",
"artifactType": "draft"
}Use cases: @planner drafts, @docs updates, @bug reports
Returns with multiple artifacts, metadata, provenance. Used by orchestrators.
{
"tier": 3,
"status": "complete",
"summary": "Sprint complete",
"artifacts": [...],
"metadata": {...},
"provenance": {...}
}Use cases: @sprint-lead sprint completion
- Tier 1 agents shouldn't need to specify artifact paths
- Tier 3 metadata is irrelevant for simple validations
- Validation can be stricter when tier is known
Use Tier 1 when:
- Agent performs analysis only (no files created)
- Returns findings, recommendations, or validation results
- Examples:
@pmvalidating requirements,@qachecking coverage,@reviewersuggesting improvements
Use Tier 2 when:
- Agent creates one artifact (document, code file, report)
- Artifact needs to be tracked for downstream processing
- Examples:
@plannercreating draft plan,@docsupdating README,@bugwriting bug report
Use Tier 3 when:
- Agent orchestrates multiple subagents
- Need to track provenance (which subagents ran, how many retries)
- Need metadata (duration, commit count, coverage delta)
- Examples:
@sprint-leadcompleting sprint,@architectgenerating multi-file solution
Decision matrix:
| Created Files | Orchestrates Others | Recommended Tier |
|---|---|---|
| 0 | No | Tier 1 |
| 1 | No | Tier 2 |
| 0+ | Yes | Tier 3 |
| Option | Pros | Cons |
|---|---|---|
| JSON files | Simple | No ACID, no queries |
| PostgreSQL | Powerful | Requires server |
| MongoDB | Flexible schema | Requires server |
| SQLite | Embedded, ACID, standard | Limited concurrency |
- Zero configuration: No server to run
- ACID guarantees: Crash-safe writes
- SQL queries: Powerful ad-hoc analysis
- WAL mode: Good read concurrency
- Portable: Single file, easy to backup
For agent workflows with single-writer, SQLite is ideal.
# Non-deterministic
event_a = {"time": datetime.now(), "type": "task_start"}
# ... some processing ...
event_b = {"time": datetime.now(), "type": "task_end"}
# Replay: wall-clock times will differclock = LogicalClock()
event_a = {"time": clock.tick(), "type": "task_start"} # time=1
# ... some processing ...
event_b = {"time": clock.tick(), "type": "task_end"} # time=2
# Replay: logical times are identicalProperties:
- If A → B, then timestamp(A) < timestamp(B)
- Monotonically increasing
- Independent of wall-clock
| Layer | Purpose | Location | Uses Tokens |
|---|---|---|---|
| Config | Project-specific values | project.config.yml |
N/A |
| Instructions | Governance rules | instructions/ |
Some |
| Skills | Agent behavior | skills/ |
Yes |
project.config.yml
↓
init.py (token substitution)
↓
resolved/skills/
resolved/instructions/
↓
.github/agents/
.github/instructions/
Layer 1: Config Validation (init.py)
↓ Command whitelist, path validation, secret scanning
Layer 2: Policy Enforcement (Rego)
↓ Business rules, composition constraints
Layer 3: Sandbox Isolation (Docker)
↓ Resource limits, network policies, capabilities
Layer 4: Audit Trail (JSONL logs)
↓ All operations logged for forensics
| Threat | Mitigation |
|---|---|
| Command injection | Whitelist + pattern detection |
| Path traversal | Validation + sandbox isolation |
| Secret exposure | Scanning + audit logging |
| Resource exhaustion | Container limits |
| Network exfiltration | Deny-by-default policies |
- Multi-agent coordination: Parallel agent execution
- Custom LLM backends: Support beyond Copilot
- Visual workflow editor: GUI for skill composition
- Plugin marketplace: Community skill sharing
- VS Code dependency: Skills are VS Code Copilot agents
- Single-writer: SQLite assumes one writer at a time
- Docker dependency: Phase 3 requires Docker
- SKILL_FLOW.md — Execution diagrams
- POLICIES.md — Policy file documentation