How it works
Quartermaster is a framework for scheduled repository maintenance. It's built around "missions" - pluggable tasks defined as directories of plain files. Each mission shares the same architecture: an AI agent that reads, and a deterministic executor that writes.
Missions
A mission is a directory containing:
missions/deps/
mission.json # {"name": "deps", "description": "..."}
system-prompt.md # Agent personality and instructions
prompt.md # Template with {repo_dir}, {existing_mrs}, etc.
allowlist.json # Commands the executor may run
skills/ # Optional Pi SDK skills for the agent
go-deps/SKILL.md
node-deps/SKILL.md
Missions are discovered at runtime from the missions/ directory. No TypeScript or recompilation needed to add one. Use --mission <name> to select which mission to run (default: deps). Use --missions-dir <dir> to point at a custom directory.
Built-in missions
deps - Scans for outdated dependencies, batches patch/minor updates into PRs with fallback strategies, and flags major version bumps as issues. Uses ecosystem-specific skills for Go, Node.js, Python, Ruby, and Rust.
docs-drift - Detects documentation that has drifted from source code changes. Compares recent git history against docs to find missing, outdated, or incorrect documentation. Creates issues (not PRs) since doc fixes need human judgment.
Architecture
┌─────────────┐ ┌──────────┐ ┌──────────┐
│ AI Agent │────>│ Plan │────>│ Executor │
│ (read-only) │ │ (JSON) │ │ (writes) │
└─────────────┘ └──────────┘ └──────────┘
Agent (scan phase):
- Loads the mission's system prompt and skills
- Explores the repo structure
- Runs read-only commands to gather information
- Checks existing PRs/issues to avoid duplicates
- Produces a typed JSON action plan via the
submit_plantool - If the plan fails validation, the agent gets the errors and fixes them (up to 3 attempts)
Executor (execute phase):
- Validates the plan against the mission's command allowlist
- Creates branches, runs whitelisted commands, tests, commits, pushes
- Creates PRs/issues via
ghorglabCLI - Falls back to individual PRs if a batch fails tests
The plan contract
The plan is a JSON file with two parts:
{
"repo_context": {
"platform": "github",
"ecosystems": ["go"],
"test_command": "go test ./...",
"default_branch": "main",
"lock_files": ["go.sum"]
},
"actions": [...]
}
Each action is one of:
| Action | What it does |
|---|---|
create_mr |
Create a branch, run commands, test, push, open PR |
update_mr |
Update an existing quartermaster PR |
create_issue |
Open an issue (for major bumps, drift findings, etc.) |
comment_mr |
Add a comment to an existing PR |
comment_issue |
Add a comment to an existing issue |
close_mr |
Close a stale quartermaster PR |
skip |
Skip an item (with reason) |
Not every mission uses every action type. For example, docs-drift only produces create_issue and skip actions.
Safety
- Command allowlist: Each mission defines its own allowlist. The executor only runs commands that match. Anything else is rejected.
- Test command allowlist: Test commands are restricted to known test runners (go, npm, bun, make, cargo, pytest, etc.).
- Branch pattern: All branches must match
quartermaster/*. - Path safety:
working_dircannot contain..or absolute paths. - Confidence threshold: Actions below the threshold are skipped.
- Dry-run default: The executor does nothing unless you pass
--execute. - Validation feedback: If the agent produces an invalid plan, it gets the errors back and can fix them (up to 3 attempts).
Grouping strategy (deps mission)
The agent decides how to group updates based on repo size:
- Small repos: Single PR with all patch/minor updates
- Monorepos: One PR per submodule with
working_dirset - Major bumps: Always an issue, never batched
- Security updates: Separate PR with high confidence
Fallback
When a batch PR has fallback_strategy: "individual_on_failure", the executor:
- Tries the batch first (all updates in one branch)
- If tests fail, deletes the branch
- Creates individual PRs for each dependency
- Each one is tested independently
Adding a mission
- Create a directory under
missions/(or a custom path) - Add
mission.json,system-prompt.md,prompt.md, andallowlist.json - Optionally add
skills/with Pi SDK skill files - Run with
--mission <name>or--missions-dir <dir>
See examples/mission-skeleton/ for a starter template, and missions/deps/ or missions/docs-drift/ for working examples.