Storage, Sync, and PMO Systems¶
Comprehensive reference for Agent Baton's data management layer: per-project SQLite databases, the central read-replica, federated sync, external source adapters, cross-project queries, the PMO portfolio overlay, and the PMO UI frontend.
1. Overview¶
Agent Baton uses a federated storage architecture where every project
maintains its own local SQLite database (baton.db) and a central
read-replica (~/.baton/central.db) aggregates data from all projects
for cross-project analytics and the PMO dashboard.
Data flow:
Project A: .claude/team-context/baton.db ─┐
Project B: .claude/team-context/baton.db ─┤── SyncEngine.push() ──> ~/.baton/central.db
Project C: .claude/team-context/baton.db ─┘ │
├── CentralStore (read-only queries)
External: Azure DevOps ── AdoAdapter ──────────────────────────────> external_* tables
│
├── baton cquery (CLI)
├── PMO Scanner (board state)
└── PMO UI (React frontend)
Key principles:
- baton.db is the write target. All execution data is written to the project's local database. Central.db is never written to by the execution engine.
- central.db is a read-only replica (with the exception of PMO tables and external source tables, which are written directly).
- Sync is one-way and incremental. SyncEngine pushes rows from project databases into central.db using rowid-based watermarks.
- SQLite with WAL mode is used everywhere for concurrent read access during execution.
2. Storage Architecture¶
2.1 Project-Level Storage (baton.db)¶
Each project stores its execution data in a single SQLite database at:
The storage subsystem supports two backends behind a common
StorageBackend protocol:
| Backend | Class | Location | Status |
|---|---|---|---|
| SQLite | SqliteStorage |
core/storage/sqlite_backend.py |
Default for all new projects |
| File | FileStorage |
core/storage/file_backend.py |
Legacy, backward-compatible |
Backend auto-detection (core/storage/__init__.py: detect_backend):
- If
baton.dbexists in the context root ->sqlite - If
execution-state.jsonorexecutions/directory exists ->file - Default for new projects ->
sqlite
The factory function get_project_storage(context_root) returns the
appropriate backend instance.
StorageBackend protocol (core/storage/protocol.py) defines the
complete interface:
- Execution state:
save_execution,load_execution,list_executions,delete_execution - Active task tracking:
set_active_task,get_active_task - Plans:
save_plan,load_plan - Results:
save_step_result,save_gate_result,save_approval_result,save_amendment - Events:
append_event,read_events - Usage/Telemetry:
log_usage,read_usage,log_telemetry,read_telemetry - Retrospectives:
save_retrospective,load_retrospective,list_retrospective_ids - Traces:
save_trace,load_trace - Patterns and Budget:
save_patterns,load_patterns,save_budget_recommendations,load_budget_recommendations - Mission Log:
append_mission_log,read_mission_log - Context:
save_context,read_context,save_profile,read_profile
2.2 ConnectionManager¶
All SQLite access goes through ConnectionManager
(core/storage/connection.py), which provides:
- Thread-safe connections: One connection per thread, cached in thread-local storage and created lazily.
- WAL journal mode: Enables concurrent reads during execution.
- Schema management: Applies DDL on first connection, runs sequential migrations when the schema version changes.
- Busy timeout: 5-second busy timeout to handle lock contention.
conn_mgr = ConnectionManager(db_path)
conn_mgr.configure_schema(PROJECT_SCHEMA_DDL, SCHEMA_VERSION)
conn = conn_mgr.get_connection() # thread-safe, lazily created
2.3 CentralStore (~/.baton/central.db)¶
CentralStore (core/storage/central.py) is the read-only query
interface for the central database at ~/.baton/central.db.
- Initialized via
ConnectionManagerwithCENTRAL_SCHEMA_DDL. - Exposes pre-built analytics views:
agent_reliability(),cost_by_task_type(),recurring_knowledge_gaps(),project_failure_rates(),external_plan_mapping(). - Provides a generic
query(sql, params)method with a read-only guard that rejects SQL statements starting with write keywords (INSERT, UPDATE, DELETE, DROP, CREATE, ALTER, REPLACE, ATTACH, DETACH). - Has a controlled
execute(sql, params)method that allows writes only to the external source tables:external_sources,external_items,external_mappings.
PMO migration: On first access via get_pmo_central_store(), the
function _maybe_migrate_pmo() automatically copies all rows from the
legacy ~/.baton/pmo.db into central.db's PMO tables using INSERT OR
REPLACE. A marker file ~/.baton/.pmo-migrated prevents re-running the
migration.
2.4 Storage Migration (JSON -> SQLite)¶
StorageMigrator (core/storage/migrate.py) handles importing legacy
JSON/JSONL flat files into baton.db:
migrator = StorageMigrator(Path(".claude/team-context"))
counts = migrator.scan() # preview what exists
imported = migrator.migrate() # do the import
verified = migrator.verify() # confirm source vs DB counts match
Import order respects foreign-key dependencies:
- Executions (parent rows for most tables)
- Events
- Usage records
- Telemetry
- Retrospectives
- Traces
- Learned patterns
- Budget recommendations
- Active task
After migration, source files can optionally be moved to
pre-sqlite-backup/.
3. Sync Engine¶
3.1 SyncEngine Overview¶
SyncEngine (core/storage/sync.py) implements one-way incremental
sync from per-project baton.db to ~/.baton/central.db.
engine = SyncEngine()
result = engine.push("my-project", Path("/path/to/project/.claude/team-context/baton.db"))
Design invariants:
- baton.db is the sole write target; central.db is a read-only replica.
- Watermarks are stored in central.db (
sync_watermarkstable). - AUTOINCREMENT
idcolumns in project tables are dropped on insert so central generates its own sequence. - Sync is idempotent: INSERT OR REPLACE for natural-PK tables, INSERT OR IGNORE for AUTOINCREMENT tables.
3.2 Sync Protocol¶
Incremental sync uses rowid-based watermarks:
- For each syncable table, read the last-synced rowid from
sync_watermarksfor the (project_id, table_name) pair. - SELECT all rows from the project table where
rowid > watermark. - INSERT into the central table with
project_idprepended to the primary key. - Update the watermark to the highest rowid seen.
- Record the sync run in
sync_history.
Column discovery is dynamic: The engine reads column names from the
source table via PRAGMA table_info at sync time, so it handles new
columns without code changes.
AUTOINCREMENT handling: For tables with AUTOINCREMENT primary keys
(gate_results, approval_results, agent_usage, telemetry, etc.), the id
column is stripped on insert. Central.db assigns its own sequence. Dedup
relies on UNIQUE constraints on the natural key columns.
3.3 Syncable Tables¶
The following tables are synced, listed in FK dependency order:
| Table | PK Columns | Auto-increment? |
|---|---|---|
executions |
task_id | No |
usage_records |
task_id | No |
retrospectives |
task_id | No |
traces |
task_id | No |
learned_patterns |
pattern_id | No |
budget_recommendations |
task_type | No |
plans |
task_id | No |
plan_phases |
task_id, phase_id | No |
plan_steps |
task_id, step_id | No |
team_members |
task_id, step_id, member_id | No |
step_results |
task_id, step_id | No |
team_step_results |
task_id, step_id, member_id | No |
gate_results |
task_id, phase_id, gate_type, checked_at | Yes |
approval_results |
task_id, phase_id, result, decided_at | Yes |
amendments |
task_id, amendment_id | No |
events |
event_id | No |
agent_usage |
task_id, agent_name | Yes |
telemetry |
task_id, timestamp, agent_name, event_type | Yes |
retrospective_outcomes |
task_id, category, agent_name | Yes |
knowledge_gaps |
task_id, description | Yes |
roster_recommendations |
task_id, action, target | Yes |
sequencing_notes |
task_id, phase, observation | Yes |
trace_events |
task_id, timestamp, event_type | Yes |
mission_log_entries |
task_id, agent_name, timestamp | Yes |
shared_context |
task_id | No |
3.4 SyncResult¶
Each sync operation returns a SyncResult:
@dataclass
class SyncResult:
project_id: str
tables_synced: int = 0
rows_synced: int = 0
duration_seconds: float = 0.0
errors: list[str] = field(default_factory=list)
@property
def success(self) -> bool:
return len(self.errors) == 0
3.5 CLI Commands¶
baton sync (default): Sync the current project, auto-detected from
the working directory. Matches the cwd against registered project paths
in central.db.
baton sync # sync current project (incremental)
baton sync --all # sync all registered projects
baton sync --project myproj # sync a specific project by ID
baton sync --rebuild # full rebuild: delete all central rows, re-sync
baton sync status # show per-table watermarks for all projects
baton sync status output shows watermarks grouped by project:
Sync Watermarks (42 entries)
Project: nds
executions rowid=15 2024-03-20T14:30:00Z
step_results rowid=48 2024-03-20T14:30:00Z
...
3.6 Auto-Sync¶
auto_sync_current_project() is a convenience function that resolves the
current project from central.db by matching the working directory against
registered project paths and runs an incremental push.
3.7 Rebuild¶
engine.rebuild(project_id, db_path) performs a full rebuild:
- DELETE all central rows for the project (across all syncable tables).
- Reset all watermarks for the project.
- Run a fresh
push()from scratch.
4. External Source Adapters¶
4.1 ExternalSourceAdapter Protocol¶
ExternalSourceAdapter (core/storage/adapters/__init__.py) is a
structural Protocol for bridging external work-tracking systems:
@runtime_checkable
class ExternalSourceAdapter(Protocol):
source_type: str # "ado" | "jira" | "github" | "linear"
def connect(self, config: dict) -> None: ...
def fetch_items(self, item_types: list[str] | None = None,
since: str | None = None) -> list[ExternalItem]: ...
def fetch_item(self, external_id: str) -> ExternalItem | None: ...
4.2 ExternalItem¶
Normalized work item from any source:
@dataclass
class ExternalItem:
source_id: str # matches external_sources.source_id
external_id: str # unique ID in the source system
item_type: str # feature | bug | epic | story | task
title: str
description: str = ""
state: str = ""
assigned_to: str = ""
priority: int = 0
parent_id: str = ""
tags: list[str] = field(default_factory=list)
url: str = ""
raw_data: dict | None = None
updated_at: str = ""
4.3 AdapterRegistry¶
Class-level registry where adapters self-register on import:
AdapterRegistry.register(AdoAdapter) # called at module level in ado.py
# Usage:
cls = AdapterRegistry.get("ado")
adapter = cls()
adapter.connect(config)
items = adapter.fetch_items()
4.4 AdoAdapter (Azure DevOps)¶
AdoAdapter (core/storage/adapters/ado.py) fetches work items via the
Azure DevOps REST API (v7.0).
Authentication: Reads a Personal Access Token from the environment
variable named by config["pat_env_var"] (default ADO_PAT). The PAT
must have at least Work Items (Read) scope. No credentials are
persisted to disk.
Configuration:
| Key | Required | Description |
|---|---|---|
organization |
Yes | ADO organization name |
project |
Yes | ADO project name |
pat_env_var |
No | Env var name for the PAT (default: ADO_PAT) |
area_path |
No | Area path filter (e.g. "MyProject\\Team") |
Type mapping (ADO -> Baton):
| ADO Work Item Type | Baton item_type |
|---|---|
| Feature | feature |
| Epic | epic |
| Bug | bug |
| User Story | story |
| Task | task |
| Issue | bug |
| Test Case | task |
| Product Backlog Item | story |
| Impediment | bug |
| (anything else) | task |
Fetch flow:
- Execute a WIQL query to get matching work item IDs.
- Fetch full field data in batches of 200 (ADO API limit).
- Normalize each item to an
ExternalItem.
4.5 Adding a New Adapter¶
To add support for a new source (e.g., Jira, GitHub, Linear):
- Create
agent_baton/core/storage/adapters/<source>.py - Implement the
ExternalSourceAdapterprotocol - Call
AdapterRegistry.register(YourAdapter)at module level - Import the module in
source_cmd.pyso it triggers registration
# agent_baton/core/storage/adapters/jira.py
class JiraAdapter:
source_type = "jira"
def connect(self, config: dict) -> None: ...
def fetch_items(self, ...) -> list[ExternalItem]: ...
def fetch_item(self, external_id: str) -> ExternalItem | None: ...
AdapterRegistry.register(JiraAdapter)
4.6 Source CLI Commands¶
# Register an external source
baton source add ado --name "My ADO" --org myorg --project MyProject --pat-env ADO_PAT
# List registered sources
baton source list
# Sync work items from a source into central.db
baton source sync ado-myorg-myproject
baton source sync --all
# Remove a source
baton source remove ado-myorg-myproject
# Map an external item to a baton plan
baton source map ado-myorg-myproject 12345 my-project task-abc --type implements
Mapping types: implements, blocks, related.
5. Cross-Project Query¶
5.1 QueryEngine (Project-Level)¶
QueryEngine (core/storage/queries.py) provides a typed, read-only
query interface for baton.db. Works with both per-project and central
databases.
Pre-built queries:
| Method | Description |
|---|---|
agent_reliability(days=30) |
Agent success rates, tokens, retries over N days |
agent_history(agent_name, limit) |
Recent step results for an agent |
task_list(status, limit) |
Recent tasks with summary, risk, agents |
task_detail(task_id) |
Full nested task detail (plan, steps, gates) |
knowledge_gaps(min_frequency) |
Recurring knowledge gaps across tasks |
roster_recommendations() |
Agent roster change recommendations |
patterns() |
Learned patterns with confidence scores |
gate_stats() |
Gate pass/fail rates by type |
cost_by_task_type() |
Token costs grouped by sequencing mode |
cost_by_agent(days) |
Token costs grouped by agent |
current_context() |
Active task, current step, current agent |
agent_briefing(agent_name) |
Markdown briefing for dispatch prompts |
raw_query(sql, params) |
Ad-hoc read-only SQL (write-guarded) |
5.2 baton cquery (Cross-Project CLI)¶
baton cquery (cli/commands/query_cmd.py) runs SQL against central.db:
# Shortcut queries
baton cquery agents # v_agent_reliability view
baton cquery costs # v_cost_by_task_type view
baton cquery gaps # v_recurring_knowledge_gaps view
baton cquery failures # v_project_failure_rate view
baton cquery mapping # v_external_plan_mapping view
# Ad-hoc SQL
baton cquery "SELECT * FROM executions LIMIT 10"
baton cquery "SELECT agent_name, COUNT(*) FROM step_results GROUP BY agent_name"
# Schema introspection
baton cquery --tables # list all tables and views
baton cquery --table executions # describe a table's columns
# Output formats
baton cquery agents --format json
baton cquery costs --format csv
baton cquery agents --format table # default
5.3 Analytics Views in central.db¶
| View | Description |
|---|---|
v_agent_reliability |
Agent success rate, avg retries, avg duration, avg tokens |
v_cost_by_task_type |
Token costs grouped by task summary and project |
v_recurring_knowledge_gaps |
Gaps appearing in 2+ projects |
v_project_failure_rate |
Per-project execution failure rates |
v_external_plan_mapping |
External items linked to baton plans |
5.4 Example Queries¶
Agent reliability across all projects:
SELECT agent_name, total_steps, success_rate, avg_tokens
FROM v_agent_reliability
WHERE total_steps >= 5
ORDER BY success_rate DESC;
Token costs by project:
SELECT project_id,
SUM(estimated_tokens) AS total_tokens,
COUNT(*) AS total_steps
FROM step_results
GROUP BY project_id
ORDER BY total_tokens DESC;
Knowledge gaps appearing in multiple projects:
SELECT description, affected_agent, project_count, projects
FROM v_recurring_knowledge_gaps
ORDER BY project_count DESC;
Recent failed executions:
SELECT project_id, task_id, status, started_at
FROM executions
WHERE status = 'failed'
ORDER BY started_at DESC
LIMIT 20;
6. PMO System¶
6.1 Data Models (agent_baton/models/pmo.py)¶
PmoProject: A project registered with the PMO.
| Field | Type | Description |
|---|---|---|
project_id |
str | Slug identifier (e.g., "nds") |
name |
str | Human-readable project name |
path |
str | Absolute filesystem path |
program |
str | Program code (e.g., "RW", "NDS") |
color |
str | Optional display color |
description |
str | Project description |
registered_at |
str | ISO 8601 registration timestamp |
ado_project |
str | Azure DevOps project name (for linking) |
PmoCard: A Kanban card representing a plan's lifecycle state.
| Field | Type | Description |
|---|---|---|
card_id |
str | task_id from MachinePlan |
project_id |
str | Parent project ID |
program |
str | Program code |
title |
str | Task summary |
column |
str | Kanban column (see below) |
risk_level |
str | LOW/MEDIUM/HIGH/CRITICAL |
priority |
int | 0=normal, 1=high, 2=critical |
agents |
list[str] | Agent names involved |
steps_completed |
int | Steps finished |
steps_total |
int | Total steps planned |
gates_passed |
int | Number of gates passed |
current_phase |
str | Current phase name |
error |
str | Last error message (if failed) |
external_id |
str | Linked external work item ID |
Kanban columns and status mapping:
| Column | Description | Mapped From (ExecutionState.status) |
|---|---|---|
queued |
Plan ready, awaiting execution | None / default |
executing |
Baton steps running | running, failed |
awaiting_human |
Paused for human input | approval_pending |
validating |
Test suites running | gate_pending |
review |
Post-execution changelist review | complete (with consolidation pending) |
deployed |
Complete and merged | complete |
PmoSignal: A signal (bug, escalation, blocker).
| Field | Type | Description |
|---|---|---|
signal_id |
str | Unique identifier |
signal_type |
str | bug, escalation, blocker |
title |
str | Short description |
description |
str | Full description |
source_project_id |
str | Originating project |
severity |
str | low, medium, high, critical |
status |
str | open, triaged, resolved |
forge_task_id |
str | If this spawned a Forge plan |
ProgramHealth: Aggregate metrics for a program.
| Field | Type | Description |
|---|---|---|
program |
str | Program code |
total_plans |
int | Total plans across all projects |
active |
int | Currently executing |
completed |
int | Deployed successfully |
blocked |
int | Awaiting human input |
failed |
int | Failed with errors |
completion_pct |
float | Percentage complete |
6.2 PMO Store¶
Two store implementations exist:
PmoStore (core/pmo/store.py) — Legacy JSON/JSONL file store:
- Config: ~/.baton/pmo-config.json (atomic write via tmp+rename)
- Archive: ~/.baton/pmo-archive.jsonl (append-only)
PmoSqliteStore (core/storage/pmo_sqlite.py) — SQLite-backed store
(current default), backed by either ~/.baton/pmo.db or
~/.baton/central.db:
- Projects: CRUD operations on the projects table
- Programs: add_program(), list_programs()
- Signals: add_signal(), resolve_signal(), get_open_signals(),
get_signal()
- Archive: archive_card(), read_archive(limit)
- Forge Sessions: create_forge_session(), complete_forge_session(),
list_forge_sessions(status)
- Metrics: record_metric(), read_metrics(metric_name, limit)
- Config compatibility: load_config(), save_config() for backward
compatibility with code that uses PmoConfig
Factory functions (core/storage/__init__.py):
# Preferred: PMO backed by central.db (auto-migrates from pmo.db)
store = get_pmo_central_store()
# Legacy: PMO backed by standalone pmo.db
store = get_pmo_storage()
# Central read-only query interface
central = get_central_storage()
# Sync engine
engine = get_sync_engine()
6.3 PMO Scanner¶
PmoScanner (core/pmo/scanner.py) scans registered projects and builds
Kanban board state.
scan_project(project):
- Detect storage backend (SQLite vs file) for the project.
- Load all ExecutionState entries from the project's storage.
- Convert each to a PmoCard using
status_to_column()mapping. - Check for saved plans without execution state (queued cards).
scan_all():
- Load all registered projects from the PMO config.
- Scan each project for active cards.
- Include archived (deployed) cards from the archive store.
- Deduplicate by card_id.
program_health():
- Groups all cards by program.
- Computes per-program metrics: total plans, active, completed, blocked, failed, completion percentage.
6.4 Smart Forge¶
ForgeSession (core/pmo/forge.py) provides AI-driven task planning
with interactive refinement. It delegates to IntelligentPlanner for plan
generation and does NOT call the Anthropic API directly.
create_plan(description, program, project_id):
- Looks up the project in the PMO store.
- Delegates to IntelligentPlanner.create_plan().
- Returns a MachinePlan for review.
save_plan(plan, project):
- Writes plan.json and plan.md to the project's team-context directory.
- Uses task-scoped directories under executions/<task-id>/.
generate_interview(plan, feedback):
- Deterministic rule-based analysis (not an LLM call).
- Examines plan structure for ambiguities and missing context.
- Returns 3-5 targeted InterviewQuestion objects.
- Questions cover: testing strategy, risk acknowledgement, multi-agent
coordination, gate definitions, scope/priority.
regenerate_plan(description, project_id, answers):
- Builds an enriched description by appending interview answers.
- Re-invokes the planner with the enriched context.
signal_to_plan(signal_id, project_id):
- Looks up a signal, generates a bug-fix plan from its description.
- Links the signal to the plan (sets forge_task_id).
- Updates signal status to "triaged".
6.5 PMO CLI Commands¶
# Start the PMO HTTP server (includes UI)
baton pmo serve [--port 8741] [--host 127.0.0.1]
# Print terminal Kanban board summary
baton pmo status
# Register a project with the PMO
baton pmo add --id nds --name "NDS Project" --path /path/to/project --program NDS [--color blue]
# Print program health bar summary
baton pmo health
baton pmo status output:
PMO Board -- 3 projects registered
nds ########.. 80% (2 active, 5 deployed, 1 blocked)
atl ####...... 40% (3 active, 2 deployed)
com .......... 0% (1 queued)
Cards:
executing task-abc 'Optimize flight ops' NDS step 3/5
deployed task-def 'Root cause tooling' ATL complete
awaiting task-ghi 'Revenue management' COM waiting
7. PMO UI (Frontend)¶
7.1 Application Architecture¶
The PMO UI is a React/Vite single-page application located at
pmo-ui/.
| Technology | Version | Purpose |
|---|---|---|
| React | ^18.3.1 | UI framework |
| Vite | ^6.0.5 | Build tool and dev server |
| TypeScript | ^5.7.2 | Type safety |
No routing library is used. The app manages two views (kanban and
forge) via React state in App.tsx.
Dark theme with a design-token system in styles/tokens.ts
(backgrounds #060a11 -> #222d42, text #f1f5f9 -> #334155, accent
#3b82f6).
7.2 Build and Development¶
cd pmo-ui
# Development (with hot reload, proxies /api to localhost:8741)
npm install
npm run dev # starts Vite dev server on port 3000
# Production build
npm run build # outputs to pmo-ui/dist/
npm run preview # preview the production build
Vite configuration (vite.config.ts):
- base: '/pmo/' -- all assets served under the /pmo/ prefix
- Dev server on port 3000 with /api proxy to http://localhost:8741
- Production build output to pmo-ui/dist/
Static file serving: When baton pmo serve starts the FastAPI server,
it mounts the built pmo-ui/dist/ directory at /pmo/ as static files
with html=True for SPA routing.
7.3 API Client¶
api/client.ts wraps all API calls to the backend:
const BASE = '/api/v1/pmo';
export const api = {
getBoard(): Promise<BoardResponse>,
getBoardByProgram(program): Promise<BoardResponse>,
getProjects(): Promise<PmoProject[]>,
getHealth(): Promise<Record<string, ProgramHealth>>,
forgePlan(body): Promise<ForgePlanResponse>,
forgeApprove(body): Promise<ForgeApproveResponse>,
forgeInterview(body): Promise<InterviewResponse>,
forgeRegenerate(body): Promise<ForgePlanResponse>,
getSignals(): Promise<PmoSignal[]>,
createSignal(body): Promise<PmoSignal>,
resolveSignal(id): Promise<PmoSignal>,
searchAdo(q): Promise<AdoSearchResponse>,
};
7.4 Key Components¶
App.tsx -- Root component. Manages navigation between the Kanban
board and The Forge. Top nav bar with "Baton PMO" branding and tab
switching.
KanbanBoard.tsx -- Main board view with:
- HealthBar showing per-program completion progress bars.
- Program filter buttons (All / per-program).
- Signals toggle button and panel.
- Status indicators (awaiting count, executing count, last updated).
- Six Kanban columns (queued, planning, executing, awaiting_human,
validating, deployed).
- "+ New Plan" button to open The Forge.
- Auto-refresh every 5 seconds via usePmoBoard hook.
KanbanCard.tsx -- Individual card display with:
- Program dot (color-coded by program name hash).
- Title, task ID, priority chip, risk level chip.
- Step progress pips (small dots showing completion).
- Current phase or error message with color-coded left border.
- Footer with project ID, agent names, and timestamp.
- Expandable detail section showing program, gates passed, and agent chips.
HealthBar.tsx -- Program health progress bars. One bar per program
with completion percentage, plan counts, and status breakdown (active,
done, blocked, failed). Color-coded by program name hash.
SignalsBar.tsx -- Expandable signals panel:
- Lists open signals with severity indicators (color-coded border).
- "Add Signal" form (inline).
- "Forge" button per signal (opens The Forge with signal context).
- "Resolve" button per signal.
ForgePanel.tsx -- AI-driven plan creation workflow with phases:
- Intake: Project selector, task type, priority, description textarea,
ADO import combobox, "Generate Plan" button.
- Generating: 5-stage SSE progress indicator (Analyzing, Routing,
Sizing, Generating, Validating) via
GET /pmo/forge/progress/{session_id}.
- Preview: PlanEditor with "Approve & Queue" and "Regenerate" buttons.
- Regenerating: InterviewPanel with refinement questions.
- Saved: Success confirmation with saved path, "New Plan" and "Back to
Board" buttons.
ExecutionProgress.tsx -- Live execution progress display:
- Step-by-step progress with status indicators.
- Interrupt controls: pause, resume, cancel buttons.
- Retry-step and skip-step for failed steps.
- Bead alert flags for warning/incident signals.
ChangelistPanel.tsx -- Post-execution code review:
- File tree grouped by contributing agent.
- Diff stats (files changed, insertions, deletions).
- Merge and Create PR action buttons.
- Presented in the "Plating Review" Kanban column.
GateApprovalPanel.tsx -- Gate approval/rejection UI:
- Displays pending gate details and output.
- Approve/reject buttons with optional notes.
- Wired into KanbanCard when card.column === 'awaiting_human'.
ReviewPanel.tsx -- Role-based review and approval:
- Request-review workflow with user identity.
- Approval log display.
- Respects BATON_APPROVAL_MODE (local vs team).
PlanEditor.tsx -- Advanced interactive plan editor:
- Stats bar (phases, steps, gates, risk level).
- Collapsible phase sections with step lists.
- Inline step editing (click to edit task description).
- Step reordering (up/down buttons).
- Add/remove steps and phases.
- Agent name chips per step.
- Model selection per step (sonnet/opus/haiku).
- Dependency multi-select across steps.
- Tag inputs for deliverables, allowed_paths, and context_files.
- Gate editing (type, command, approval_required).
InterviewPanel.tsx -- Refinement questions panel:
- Numbered question cards with context.
- Choice-based answers (button selection) or text input.
- "Skip" option for each question.
- Submit button with answer count.
PlanPreview.tsx -- Read-only plan display with stat tiles, summary
section, and phase/step breakdown.
AdoCombobox.tsx -- Azure DevOps work item search with debounced
typeahead (300ms). Currently uses mock data (placeholder endpoint).
7.5 Data Flow Hook¶
usePmoBoard (hooks/usePmoBoard.ts) manages board state:
- Polls
GET /api/v1/pmo/boardevery 5 seconds (POLL_INTERVAL_MS). - Returns
{ cards, health, loading, error, refresh, lastUpdated }. - Handles mount/unmount lifecycle to prevent state updates on unmounted components.
- Optionally filters by program via
getBoardByProgram.
7.6 PMO API Endpoints¶
All endpoints are prefixed with /api/v1 and defined in
agent_baton/api/routes/pmo.py. 36 endpoints total.
| Method | Path | Description |
|---|---|---|
| Board | ||
| GET | /pmo/board |
Full Kanban board (cards + health) |
| GET | /pmo/board/{program} |
Board filtered by program |
| GET | /pmo/cards/{card_id} |
Card detail |
| GET | /pmo/cards/{card_id}/execution |
Card execution state |
| GET | /pmo/events |
SSE stream of board-relevant events |
| Projects | ||
| GET | /pmo/projects |
List registered projects |
| POST | /pmo/projects |
Register a project |
| DELETE | /pmo/projects/{project_id} |
Unregister a project |
| GET | /pmo/health |
Program health metrics |
| Forge | ||
| POST | /pmo/forge/plan |
Create a plan via IntelligentPlanner |
| GET | /pmo/forge/progress/{session_id} |
SSE stream of plan-generation progress (5 stages) |
| POST | /pmo/forge/approve |
Save approved plan to project |
| POST | /pmo/forge/interview |
Generate refinement questions |
| POST | /pmo/forge/regenerate |
Re-generate plan with answers |
| Execution Controls | ||
| POST | /pmo/execute/{card_id} |
Launch execution for a card |
| POST | /pmo/execute/{card_id}/pause |
Pause worker (SIGSTOP) |
| POST | /pmo/execute/{card_id}/resume |
Resume worker (SIGCONT) |
| POST | /pmo/execute/{card_id}/cancel |
Cancel worker (SIGTERM) |
| POST | /pmo/execute/{card_id}/retry-step |
Reset failed step for re-dispatch |
| POST | /pmo/execute/{card_id}/skip-step |
Skip failed step |
| Gates | ||
| GET | /pmo/gates/pending |
List executions awaiting gate approval |
| POST | /pmo/gates/{task_id}/approve |
Approve a pending gate |
| POST | /pmo/gates/{task_id}/reject |
Reject a pending gate |
| Changelist and Merge | ||
| GET | /pmo/cards/{card_id}/changelist |
Consolidation changelist (files by agent) |
| POST | /pmo/cards/{card_id}/merge |
Fast-forward merge onto base branch |
| POST | /pmo/cards/{card_id}/create-pr |
Create GitHub PR via gh |
| Review and Approval | ||
| POST | /pmo/cards/{card_id}/request-review |
Request review (writes approval_log) |
| GET | /pmo/cards/{card_id}/approval-log |
Approval audit trail |
| External Items | ||
| GET | /pmo/ado/search |
Search ADO items |
| GET | /pmo/external-items |
List external items |
| GET | /pmo/external-items/{item_id}/mappings |
Item-to-plan mappings |
| Signals | ||
| GET | /pmo/signals |
List open signals |
| POST | /pmo/signals |
Create a signal |
| POST | /pmo/signals/batch/resolve |
Batch-resolve signals |
| POST | /pmo/signals/{id}/resolve |
Resolve a signal |
| POST | /pmo/signals/{id}/forge |
Triage signal into a plan |
Dependency injection: The API server uses FastAPI's Depends() system
with singletons initialized in api/deps.py:
- get_pmo_store() -- PmoSqliteStore backed by central.db
- get_pmo_scanner() -- PmoScanner wired to the store
- get_forge_session() -- ForgeSession wired to the planner and store
8. Database Schema Reference¶
Schema version: 2 (defined in core/storage/schema.py).
Three schema definitions exist:
PROJECT_SCHEMA_DDL-- per-project baton.dbPMO_SCHEMA_DDL-- global pmo.db (legacy, migrated to central)CENTRAL_SCHEMA_DDL-- global central.db
8.1 Project Database (baton.db)¶
_schema_version -- Schema version tracking.
| Column | Type | Notes |
|---|---|---|
| version | INTEGER | Current schema version |
executions -- Replaces execution-state.json.
| Column | Type | Notes |
|---|---|---|
| task_id | TEXT | PK |
| status | TEXT | running, complete, failed, gate_pending, approval_pending |
| current_phase | INTEGER | Current phase index |
| current_step_index | INTEGER | Current step within phase |
| started_at | TEXT | ISO 8601 |
| completed_at | TEXT | Nullable |
| created_at | TEXT | Auto-set |
| updated_at | TEXT | Auto-set |
| pending_gaps | TEXT | JSON array of KnowledgeGapSignal (v2) |
| resolved_decisions | TEXT | JSON array of ResolvedDecision (v2) |
Indexes: idx_executions_status, idx_executions_started
plans -- Replaces plan.json.
| Column | Type | Notes |
|---|---|---|
| task_id | TEXT | PK, FK -> executions |
| task_summary | TEXT | Natural-language description |
| risk_level | TEXT | LOW, MEDIUM, HIGH, CRITICAL |
| budget_tier | TEXT | minimal, standard, premium |
| execution_mode | TEXT | phased, concurrent |
| git_strategy | TEXT | commit-per-agent, feature-branch |
| shared_context | TEXT | Context shared across agents |
| pattern_source | TEXT | Source pattern ID, nullable |
| plan_markdown | TEXT | Human-readable plan |
| created_at | TEXT | ISO 8601 |
| explicit_knowledge_packs | TEXT | JSON array (v2) |
| explicit_knowledge_docs | TEXT | JSON array (v2) |
| intervention_level | TEXT | low, medium, high (v2) |
| task_type | TEXT | Nullable (v2) |
plan_phases
| Column | Type | Notes |
|---|---|---|
| task_id | TEXT | PK (composite), FK -> plans |
| phase_id | INTEGER | PK (composite) |
| name | TEXT | Phase name |
| approval_required | INTEGER | Boolean (0/1) |
| approval_description | TEXT | Why approval is needed |
| gate_type | TEXT | pytest, custom, etc. |
| gate_command | TEXT | Shell command to run |
| gate_description | TEXT | Human description |
| gate_fail_on | TEXT | JSON array of failure patterns |
plan_steps
| Column | Type | Notes |
|---|---|---|
| task_id | TEXT | PK (composite), FK -> plan_phases |
| step_id | TEXT | PK (composite) |
| phase_id | INTEGER | FK -> plan_phases |
| agent_name | TEXT | Agent to dispatch |
| task_description | TEXT | What the agent should do |
| model | TEXT | sonnet, opus, haiku |
| depends_on | TEXT | JSON array of step_ids |
| deliverables | TEXT | JSON array |
| allowed_paths | TEXT | JSON array of file paths |
| blocked_paths | TEXT | JSON array of file paths |
| context_files | TEXT | JSON array of file paths |
| knowledge_attachments | TEXT | JSON array (v2) |
Indexes: idx_plan_steps_agent, idx_plan_steps_phase
team_members -- Team members within a step (for concurrent steps).
| Column | Type | Notes |
|---|---|---|
| task_id, step_id, member_id | TEXT | PK (composite) |
| agent_name | TEXT | |
| role | TEXT | implementer, reviewer, etc. |
| task_description | TEXT | |
| model | TEXT | |
| depends_on | TEXT | JSON array |
| deliverables | TEXT | JSON array |
step_results
| Column | Type | Notes |
|---|---|---|
| task_id | TEXT | PK (composite), FK -> executions |
| step_id | TEXT | PK (composite) |
| agent_name | TEXT | |
| status | TEXT | complete, failed |
| outcome | TEXT | Summary of what was done |
| files_changed | TEXT | JSON array |
| commit_hash | TEXT | |
| estimated_tokens | INTEGER | |
| duration_seconds | REAL | |
| retries | INTEGER | |
| error | TEXT | Error message if failed |
| completed_at | TEXT |
Indexes: idx_step_results_status, idx_step_results_agent
team_step_results -- Per-member results within a team step.
| Column | Type | Notes |
|---|---|---|
| task_id, step_id, member_id | TEXT | PK (composite) |
| agent_name | TEXT | |
| status | TEXT | |
| outcome | TEXT | |
| files_changed | TEXT | JSON array |
gate_results
| Column | Type | Notes |
|---|---|---|
| id | INTEGER | PK AUTOINCREMENT |
| task_id | TEXT | FK -> executions |
| phase_id | INTEGER | |
| gate_type | TEXT | pytest, custom |
| passed | INTEGER | Boolean (0/1) |
| output | TEXT | Gate output/log |
| checked_at | TEXT |
approval_results
| Column | Type | Notes |
|---|---|---|
| id | INTEGER | PK AUTOINCREMENT |
| task_id | TEXT | FK -> executions |
| phase_id | INTEGER | |
| result | TEXT | approve, reject, approve-with-feedback |
| feedback | TEXT | |
| decided_at | TEXT |
amendments -- Runtime plan modifications.
| Column | Type | Notes |
|---|---|---|
| task_id, amendment_id | TEXT | PK (composite) |
| trigger | TEXT | What triggered the amendment |
| trigger_phase_id | INTEGER | |
| description | TEXT | |
| phases_added | TEXT | JSON array |
| steps_added | TEXT | JSON array |
| feedback | TEXT | |
| created_at | TEXT |
events
| Column | Type | Notes |
|---|---|---|
| event_id | TEXT | PK |
| task_id | TEXT | |
| timestamp | TEXT | |
| topic | TEXT | |
| sequence | INTEGER | |
| payload | TEXT | JSON |
Indexes: idx_events_task, idx_events_topic, idx_events_task_seq
usage_records
| Column | Type | Notes |
|---|---|---|
| task_id | TEXT | PK |
| timestamp | TEXT | |
| total_agents | INTEGER | |
| risk_level | TEXT | |
| sequencing_mode | TEXT | phased_delivery, concurrent |
| gates_passed | INTEGER | |
| gates_failed | INTEGER | |
| outcome | TEXT | |
| notes | TEXT |
agent_usage
| Column | Type | Notes |
|---|---|---|
| id | INTEGER | PK AUTOINCREMENT |
| task_id | TEXT | FK -> usage_records |
| agent_name | TEXT | |
| model | TEXT | |
| steps | INTEGER | |
| retries | INTEGER | |
| gate_results | TEXT | JSON array |
| estimated_tokens | INTEGER | |
| duration_seconds | REAL |
telemetry
| Column | Type | Notes |
|---|---|---|
| id | INTEGER | PK AUTOINCREMENT |
| timestamp | TEXT | |
| agent_name | TEXT | |
| event_type | TEXT | |
| tool_name | TEXT | |
| file_path | TEXT | |
| duration_ms | INTEGER | |
| details | TEXT | |
| task_id | TEXT |
retrospectives
| Column | Type | Notes |
|---|---|---|
| task_id | TEXT | PK |
| task_name | TEXT | |
| timestamp | TEXT | |
| agent_count | INTEGER | |
| retry_count | INTEGER | |
| gates_passed | INTEGER | |
| gates_failed | INTEGER | |
| risk_level | TEXT | |
| duration_estimate | TEXT | |
| estimated_tokens | INTEGER | |
| markdown | TEXT | Full retrospective markdown |
retrospective_outcomes
| Column | Type | Notes |
|---|---|---|
| id | INTEGER | PK AUTOINCREMENT |
| task_id | TEXT | FK -> retrospectives |
| category | TEXT | worked, didnt_work |
| agent_name | TEXT | |
| worked_well | TEXT | |
| issues | TEXT | |
| root_cause | TEXT |
knowledge_gaps
| Column | Type | Notes |
|---|---|---|
| id | INTEGER | PK AUTOINCREMENT |
| task_id | TEXT | FK -> retrospectives |
| description | TEXT | |
| affected_agent | TEXT | |
| suggested_fix | TEXT |
roster_recommendations
| Column | Type | Notes |
|---|---|---|
| id | INTEGER | PK AUTOINCREMENT |
| task_id | TEXT | FK -> retrospectives |
| action | TEXT | add, remove, retrain |
| target | TEXT | Agent name |
| reason | TEXT |
sequencing_notes
| Column | Type | Notes |
|---|---|---|
| id | INTEGER | PK AUTOINCREMENT |
| task_id | TEXT | FK -> retrospectives |
| phase | TEXT | |
| observation | TEXT | |
| keep | INTEGER | Boolean (0/1) |
traces
| Column | Type | Notes |
|---|---|---|
| task_id | TEXT | PK |
| plan_snapshot | TEXT | JSON |
| started_at | TEXT | |
| completed_at | TEXT | Nullable |
| outcome | TEXT | Nullable |
trace_events
| Column | Type | Notes |
|---|---|---|
| id | INTEGER | PK AUTOINCREMENT |
| task_id | TEXT | FK -> traces |
| timestamp | TEXT | |
| event_type | TEXT | |
| agent_name | TEXT | Nullable |
| phase | INTEGER | |
| step | INTEGER | |
| details | TEXT | JSON |
| duration_seconds | REAL | Nullable |
learned_patterns
| Column | Type | Notes |
|---|---|---|
| pattern_id | TEXT | PK |
| task_type | TEXT | |
| stack | TEXT | Nullable |
| recommended_template | TEXT | |
| recommended_agents | TEXT | JSON array |
| confidence | REAL | 0.0 - 1.0 |
| sample_size | INTEGER | |
| success_rate | REAL | 0.0 - 1.0 |
| avg_token_cost | INTEGER | |
| evidence | TEXT | JSON array |
| created_at | TEXT | |
| updated_at | TEXT |
budget_recommendations
| Column | Type | Notes |
|---|---|---|
| task_type | TEXT | PK |
| current_tier | TEXT | |
| recommended_tier | TEXT | |
| reason | TEXT | |
| avg_tokens_used | INTEGER | |
| median_tokens_used | INTEGER | |
| p95_tokens_used | INTEGER | |
| sample_size | INTEGER | |
| confidence | REAL | |
| potential_savings | INTEGER |
mission_log_entries
| Column | Type | Notes |
|---|---|---|
| id | INTEGER | PK AUTOINCREMENT |
| task_id | TEXT | FK -> executions |
| agent_name | TEXT | |
| status | TEXT | |
| assignment | TEXT | |
| result | TEXT | |
| files | TEXT | JSON array |
| decisions | TEXT | JSON array |
| issues | TEXT | JSON array |
| handoff | TEXT | |
| commit_hash | TEXT | |
| failure_class | TEXT | Nullable |
| timestamp | TEXT |
shared_context
| Column | Type | Notes |
|---|---|---|
| task_id | TEXT | PK, FK -> executions |
| content | TEXT | Full context blob |
| task_title | TEXT | |
| stack | TEXT | |
| architecture | TEXT | |
| conventions | TEXT | |
| guardrails | TEXT | |
| agent_assignments | TEXT | |
| domain_context | TEXT | |
| updated_at | TEXT | Auto-set |
codebase_profile -- Singleton table.
| Column | Type | Notes |
|---|---|---|
| id | INTEGER | PK CHECK (id = 1) |
| content | TEXT | Profile content |
| updated_at | TEXT | Auto-set |
active_task -- Singleton table.
| Column | Type | Notes |
|---|---|---|
| id | INTEGER | PK CHECK (id = 1) |
| task_id | TEXT | Active task ID |
8.2 PMO Database (pmo.db / central.db PMO tables)¶
projects
| Column | Type | Notes |
|---|---|---|
| project_id | TEXT | PK |
| name | TEXT | |
| path | TEXT | Absolute filesystem path |
| program | TEXT | |
| color | TEXT | |
| description | TEXT | |
| registered_at | TEXT | ISO 8601 |
| ado_project | TEXT | For ADO linking |
programs
| Column | Type | Notes |
|---|---|---|
| name | TEXT | PK |
signals
| Column | Type | Notes |
|---|---|---|
| signal_id | TEXT | PK |
| signal_type | TEXT | bug, escalation, blocker |
| title | TEXT | |
| description | TEXT | |
| source_project_id | TEXT | |
| severity | TEXT | low, medium, high, critical |
| status | TEXT | open, triaged, resolved |
| created_at | TEXT | |
| resolved_at | TEXT | |
| forge_task_id | TEXT | Linked plan task_id |
archived_cards
| Column | Type | Notes |
|---|---|---|
| card_id | TEXT | PK |
| project_id | TEXT | |
| program | TEXT | |
| title | TEXT | |
| column_name | TEXT | |
| risk_level | TEXT | |
| priority | INTEGER | |
| agents | TEXT | JSON array |
| steps_completed | INTEGER | |
| steps_total | INTEGER | |
| gates_passed | INTEGER | |
| current_phase | TEXT | |
| error | TEXT | |
| created_at | TEXT | |
| updated_at | TEXT | |
| external_id | TEXT |
forge_sessions
| Column | Type | Notes |
|---|---|---|
| session_id | TEXT | PK |
| project_id | TEXT | |
| title | TEXT | |
| status | TEXT | active, completed |
| created_at | TEXT | |
| completed_at | TEXT | Nullable |
| task_id | TEXT | Resulting plan task_id |
| notes | TEXT |
pmo_metrics
| Column | Type | Notes |
|---|---|---|
| id | INTEGER | PK AUTOINCREMENT |
| timestamp | TEXT | |
| program | TEXT | |
| metric_name | TEXT | |
| metric_value | REAL | |
| details | TEXT | JSON |
users -- PMO user identity and role tracking.
| Column | Type | Notes |
|---|---|---|
| user_id | TEXT | PK |
| display_name | TEXT | |
| role | TEXT | creator, reviewer, approver, admin |
| created_at | TEXT | ISO 8601 |
Index: idx_users_role
approval_log -- Immutable audit trail of approve/reject/request_review actions.
| Column | Type | Notes |
|---|---|---|
| log_id | TEXT | PK |
| task_id | TEXT | Card/task being reviewed |
| phase_id | TEXT | Phase within the task |
| user_id | TEXT | Who performed the action |
| action | TEXT | approve, reject, request_review |
| notes | TEXT | Optional reviewer notes |
| created_at | TEXT | ISO 8601 |
Indexes: idx_approval_log_task, idx_approval_log_user
8.3 Central Database Sync Infrastructure¶
sync_watermarks -- Per-table watermarks tracking the highest synced
rowid.
| Column | Type | Notes |
|---|---|---|
| project_id | TEXT | PK (composite) |
| table_name | TEXT | PK (composite) |
| last_rowid | INTEGER | Highest synced rowid |
| last_synced | TEXT | ISO 8601 |
sync_history -- History of sync runs for observability.
| Column | Type | Notes |
|---|---|---|
| id | INTEGER | PK AUTOINCREMENT |
| project_id | TEXT | |
| started_at | TEXT | |
| completed_at | TEXT | |
| status | TEXT | running, success, partial |
| rows_synced | INTEGER | |
| tables_synced | INTEGER | |
| error | TEXT | |
| trigger | TEXT | manual, auto |
8.4 Central Database External Source Tables¶
external_sources -- Registered external work-tracking connections.
| Column | Type | Notes |
|---|---|---|
| source_id | TEXT | PK |
| source_type | TEXT | ado, jira, github, linear |
| display_name | TEXT | Human-readable name |
| config | TEXT | JSON with connection params |
| last_synced | TEXT | |
| enabled | INTEGER | Boolean (0/1) |
external_items -- Cached work items from external sources.
| Column | Type | Notes |
|---|---|---|
| id | INTEGER | PK AUTOINCREMENT |
| source_id | TEXT | FK -> external_sources |
| external_id | TEXT | ID in the source system |
| item_type | TEXT | feature, bug, epic, story, task |
| title | TEXT | |
| description | TEXT | |
| state | TEXT | Workflow state |
| assigned_to | TEXT | |
| priority | TEXT | |
| parent_id | TEXT | Parent item external_id |
| tags | TEXT | JSON array |
| url | TEXT | Link to source UI |
| raw_data | TEXT | Full JSON from source API |
| fetched_at | TEXT | |
| updated_at | TEXT |
UNIQUE constraint: (source_id, external_id)
external_mappings -- Links between external items and baton plans.
| Column | Type | Notes |
|---|---|---|
| id | INTEGER | PK AUTOINCREMENT |
| source_id | TEXT | |
| external_id | TEXT | |
| project_id | TEXT | Baton project ID |
| task_id | TEXT | Baton task/execution ID |
| mapping_type | TEXT | implements, blocks, related |
| created_at | TEXT |
UNIQUE constraint: (source_id, external_id, task_id)
8.5 Central Database Synced Tables¶
All synced project tables in central.db mirror the project-level schema
with project_id prepended to the primary key. The composite PK becomes
(project_id, <original_pk_columns>).
8.6 Central Database Analytics Views¶
v_agent_reliability:
SELECT agent_name, total_steps, successful_steps, success_rate,
avg_retries, avg_duration_seconds, avg_tokens
FROM step_results GROUP BY agent_name
v_cost_by_task_type:
SELECT task_summary AS task_type_hint, task_count, total_tokens,
avg_tokens_per_agent, total_duration_seconds, project_id
FROM plans JOIN agent_usage GROUP BY project_id, task_summary
v_recurring_knowledge_gaps:
SELECT description, affected_agent, project_count, projects
FROM knowledge_gaps GROUP BY description, affected_agent
HAVING project_count >= 2
v_project_failure_rate:
SELECT project_id, total_executions, failed_executions, failure_rate
FROM executions GROUP BY project_id
v_external_plan_mapping:
SELECT source_id, external_id, external_title, external_state,
source_type, source_name, project_id, task_id,
mapping_type, plan_summary
FROM external_mappings JOIN external_sources
LEFT JOIN external_items LEFT JOIN plans
8.7 Schema Migrations¶
Migrations are stored in schema.py as a dict keyed by version number:
SCHEMA_VERSION = 2
MIGRATIONS: dict[int, str] = {
2: """
ALTER TABLE plans ADD COLUMN explicit_knowledge_packs TEXT NOT NULL DEFAULT '[]';
ALTER TABLE plans ADD COLUMN explicit_knowledge_docs TEXT NOT NULL DEFAULT '[]';
ALTER TABLE plans ADD COLUMN intervention_level TEXT NOT NULL DEFAULT 'low';
ALTER TABLE plans ADD COLUMN task_type TEXT;
ALTER TABLE plan_steps ADD COLUMN knowledge_attachments TEXT NOT NULL DEFAULT '[]';
ALTER TABLE executions ADD COLUMN pending_gaps TEXT NOT NULL DEFAULT '[]';
ALTER TABLE executions ADD COLUMN resolved_decisions TEXT NOT NULL DEFAULT '[]';
""",
}
The ConnectionManager._run_migrations() method applies these
sequentially when the stored version is less than SCHEMA_VERSION.
9. Configuration¶
9.1 Storage Paths¶
| Path | Purpose |
|---|---|
<project>/.claude/team-context/baton.db |
Per-project execution data |
<project>/.claude/team-context/ |
Legacy JSON/JSONL files |
~/.baton/central.db |
Central read-replica + PMO + external sources |
~/.baton/pmo.db |
Legacy PMO database (migrated to central.db) |
~/.baton/pmo-config.json |
Legacy PMO config (JSON file store) |
~/.baton/pmo-archive.jsonl |
Legacy PMO archive |
~/.baton/.pmo-migrated |
Migration marker file |
9.2 PMO Server¶
| Setting | Default | CLI Flag |
|---|---|---|
| Host | 127.0.0.1 |
--host |
| Port | 8741 |
--port |
| Auth token | None (disabled) | token param in create_app() |
9.3 Sync Configuration¶
Sync is currently manual (triggered via baton sync). There is no
automatic sync daemon. The auto_sync_current_project() function can be
called from hooks or scripts to sync after each execution completes.
9.4 External Source Configuration¶
External source configs are stored as JSON in external_sources.config:
Credentials are read from environment variables at runtime and never persisted to disk.
10. Troubleshooting¶
Storage Issues¶
"central.db not found":
Run baton pmo add to register at least one project, which creates
central.db.
"Project DB not found":
Ensure .claude/team-context/baton.db exists in the project directory.
If the project uses legacy file storage, run the migration:
from agent_baton.core.storage.migrate import StorageMigrator
migrator = StorageMigrator(Path(".claude/team-context"))
migrator.migrate()
"database is locked":
SQLite WAL mode is enabled with a 5-second busy timeout. If locks persist,
check for zombie processes holding the database open. The
ConnectionManager uses timeout=10.0 on connections and
busy_timeout=5000 PRAGMA.
Schema version mismatch:
If _schema_version in the database is older than SCHEMA_VERSION in
code, migrations run automatically on next connection. If the database
has a newer version than the code, you may need to update agent-baton.
Sync Issues¶
"No projects registered in central.db":
Register projects first with baton pmo add.
"Could not detect current project":
The auto-detect matches os.getcwd() against registered project paths.
Ensure you are inside a registered project directory.
Partial sync (some tables fail):
SyncResult.errors lists which tables failed. Common cause: schema
mismatch between project baton.db and central.db. Re-run with --rebuild
to force a full re-sync.
Watermark reset:
To force a full re-sync without losing other projects' data, use
--rebuild which deletes all central rows for the project and resets
watermarks:
PMO Issues¶
"PMO Board -- no projects registered":
Run baton pmo add --id ID --name NAME --path PATH --program PROG.
"Install API extras": The PMO server requires FastAPI and uvicorn:
PMO UI shows "API 500":
Check that the backend server is running (baton pmo serve) and that
central.db is accessible. The UI proxies API calls to localhost:8741
during development.
Legacy pmo.db migration:
If ~/.baton/pmo.db exists and ~/.baton/.pmo-migrated does not,
the migration runs automatically on first get_pmo_central_store() call.
To force re-migration, delete the marker file:
External Source Issues¶
"ADO PAT not found":
Set the environment variable specified by pat_env_var (default
ADO_PAT):
"No adapter available": Only the ADO adapter is currently implemented. For other sources, see section 4.5 on adding new adapters.
"WIQL query failed": Check that the PAT has at least Work Items (Read) scope and that the organization and project names match exactly.
File Reference¶
| File | Purpose |
|---|---|
agent_baton/core/storage/__init__.py |
Factory functions, backend detection |
agent_baton/core/storage/protocol.py |
StorageBackend protocol definition |
agent_baton/core/storage/sqlite_backend.py |
SqliteStorage (project baton.db) |
agent_baton/core/storage/file_backend.py |
FileStorage (legacy JSON/JSONL) |
agent_baton/core/storage/connection.py |
ConnectionManager (thread-safe SQLite) |
agent_baton/core/storage/schema.py |
All DDL schemas and migrations |
agent_baton/core/storage/central.py |
CentralStore (read-only central.db) |
agent_baton/core/storage/sync.py |
SyncEngine (project -> central sync) |
agent_baton/core/storage/pmo_sqlite.py |
PmoSqliteStore (PMO SQLite store) |
agent_baton/core/storage/migrate.py |
StorageMigrator (JSON -> SQLite) |
agent_baton/core/storage/queries.py |
QueryEngine (typed read-only queries) |
agent_baton/core/storage/adapters/__init__.py |
ExternalSourceAdapter protocol, registry |
agent_baton/core/storage/adapters/ado.py |
AdoAdapter (Azure DevOps) |
agent_baton/core/pmo/__init__.py |
PMO subsystem package |
agent_baton/core/pmo/store.py |
PmoStore (legacy JSON file store) |
agent_baton/core/pmo/scanner.py |
PmoScanner (project scanning, board state) |
agent_baton/core/pmo/forge.py |
ForgeSession (AI-driven plan creation) |
agent_baton/models/pmo.py |
PMO data models (PmoCard, PmoProject, etc.) |
agent_baton/cli/commands/sync_cmd.py |
baton sync CLI command |
agent_baton/cli/commands/query_cmd.py |
baton cquery CLI command |
agent_baton/cli/commands/source_cmd.py |
baton source CLI command |
agent_baton/cli/commands/pmo_cmd.py |
baton pmo CLI command |
agent_baton/api/routes/pmo.py |
PMO API endpoints |
agent_baton/api/server.py |
FastAPI app factory (mounts PMO UI) |
agent_baton/api/deps.py |
Dependency injection (PMO singletons) |
pmo-ui/src/App.tsx |
React root component |
pmo-ui/src/api/client.ts |
API client wrapper |
pmo-ui/src/api/types.ts |
TypeScript type definitions |
pmo-ui/src/hooks/usePmoBoard.ts |
Board polling hook |
pmo-ui/src/components/KanbanBoard.tsx |
Main board view |
pmo-ui/src/components/KanbanCard.tsx |
Card display |
pmo-ui/src/components/ForgePanel.tsx |
Plan creation workflow |
pmo-ui/src/components/PlanEditor.tsx |
Interactive plan editor |
pmo-ui/src/components/InterviewPanel.tsx |
Refinement questions |
pmo-ui/src/components/SignalsBar.tsx |
Signals panel |
pmo-ui/src/components/HealthBar.tsx |
Program health bars |
pmo-ui/src/components/AdoCombobox.tsx |
ADO search combobox |
pmo-ui/src/components/PlanPreview.tsx |
Read-only plan display |
pmo-ui/src/components/ChangelistPanel.tsx |
Post-execution code review |
pmo-ui/src/components/ExecutionProgress.tsx |
Live execution with interrupt controls |
pmo-ui/src/components/GateApprovalPanel.tsx |
Gate approval/rejection UI |
pmo-ui/src/components/ReviewPanel.tsx |
Role-based review and approval |
pmo-ui/src/components/AnalyticsDashboard.tsx |
Program analytics |
pmo-ui/src/styles/tokens.ts |
Design tokens and column definitions |
agent_baton/api/middleware/user_identity.py |
UserIdentityMiddleware |
pmo-ui/vite.config.ts |
Vite build configuration |
pmo-ui/package.json |
Frontend dependencies |