Skip to content

[plan][feat]: Introduce .agentize.local.yaml as unified local configuration standard #600

@were

Description

@were

Description

Consolidate developer-specific configuration (handsoff, supervisor, Telegram, server runtime) into .agentize.local.yaml, making docs YAML-first while preserving env-var overrides for compatibility. This addresses roadmap items P2.4 (Configurable Permission Rules) and P2.8 (Multi-Repository Support) from issue #598.

Problem: Configuration is scattered across 28+ environment variables documented in docs/envvar.md, making it difficult to manage developer-specific settings consistently.

Solution: Extend the existing .agentize.local.yaml infrastructure to support handsoff and Telegram settings, with environment variables remaining as overrides for backward compatibility.

Proposed Solution

Consensus Summary

Adopt a YAML-first, minimal-dependency approach by extending the existing .agentize.local.yaml loader and wiring it into the Python server and Claude plugin hooks, while keeping environment variables as overrides for backward compatibility.


YAML Schema Specification

Complete .agentize.local.yaml Structure

# =============================================================================
# .agentize.local.yaml - Developer-specific local configuration
# =============================================================================
# This file is gitignored and contains secrets/personal preferences.
# Environment variables can override any setting (higher precedence).
# =============================================================================

# -----------------------------------------------------------------------------
# Handsoff Mode - Automatic workflow continuation
# -----------------------------------------------------------------------------
handsoff:
  enabled: true                    # HANDSOFF_MODE (default: true)
  max_continuations: 10            # HANDSOFF_MAX_CONTINUATIONS (default: 10)
  auto_permission: true            # HANDSOFF_AUTO_PERMISSION (default: true)
  debug: false                     # HANDSOFF_DEBUG (default: false)
  supervisor:
    provider: claude               # HANDSOFF_SUPERVISOR (default: none)
    model: opus                    # HANDSOFF_SUPERVISOR_MODEL (default: provider-specific)
    flags: "--timeout 1800"        # HANDSOFF_SUPERVISOR_FLAGS (default: "")

# -----------------------------------------------------------------------------
# Telegram Approval - Remote approval via Telegram bot
# -----------------------------------------------------------------------------
telegram:
  enabled: false                   # AGENTIZE_USE_TG (default: false)
  token: "123456:ABC-DEF..."       # TG_API_TOKEN (required when enabled)
  chat_id: "-1001234567890"        # TG_CHAT_ID (required when enabled)
  timeout_sec: 60                  # TG_APPROVAL_TIMEOUT_SEC (default: 60, max: 7200)
  poll_interval_sec: 5             # TG_POLL_INTERVAL_SEC (default: 5)
  allowed_user_ids: "123,456,789"  # TG_ALLOWED_USER_IDS (CSV string, optional)

# -----------------------------------------------------------------------------
# Server Runtime - lol serve configuration
# -----------------------------------------------------------------------------
server:
  period: 5m                       # Polling interval (default: 5m)
  num_workers: 5                   # Worker pool size (default: 5)

# -----------------------------------------------------------------------------
# Workflow Model Assignments - Model selection per workflow type
# -----------------------------------------------------------------------------
workflows:
  impl:
    model: opus                    # Model for implementation workflows
  refine:
    model: sonnet                  # Model for refinement workflows

Environment Variable to YAML Mapping

Environment Variable YAML Path Type Default Scope
Handsoff Mode
HANDSOFF_MODE handsoff.enabled bool true YAML + env override
HANDSOFF_MAX_CONTINUATIONS handsoff.max_continuations int 10 YAML + env override
HANDSOFF_AUTO_PERMISSION handsoff.auto_permission bool true YAML + env override
HANDSOFF_DEBUG handsoff.debug bool false YAML + env override
HANDSOFF_SUPERVISOR handsoff.supervisor.provider string none YAML + env override
HANDSOFF_SUPERVISOR_MODEL handsoff.supervisor.model string provider-specific YAML + env override
HANDSOFF_SUPERVISOR_FLAGS handsoff.supervisor.flags string "" YAML + env override
Telegram Approval
AGENTIZE_USE_TG telegram.enabled bool false YAML + env override
TG_API_TOKEN telegram.token string - YAML + env override
TG_CHAT_ID telegram.chat_id string - YAML + env override
TG_APPROVAL_TIMEOUT_SEC telegram.timeout_sec int 60 YAML + env override
TG_POLL_INTERVAL_SEC telegram.poll_interval_sec int 5 YAML + env override
TG_ALLOWED_USER_IDS telegram.allowed_user_ids CSV string - YAML + env override
Server Runtime
(CLI only) server.period string 5m YAML only
(CLI only) server.num_workers int 5 YAML only
Env-Only Variables (not in YAML)
AGENTIZE_HOME - path - env-only (setup.sh)
PYTHONPATH - path - env-only (setup.sh)
WT_DEFAULT_BRANCH - string main env-only (shell scripts)
WT_CURRENT_WORKTREE - path - env-only (auto-set)
TEST_SHELLS - string - env-only (test suite)

Precedence Order (highest to lowest)

1. CLI arguments (e.g., --tg-token)
2. Environment variables (e.g., TG_API_TOKEN)
3. .agentize.local.yaml
4. Default values

Type Coercion Rules

Type Accepted Values Example
bool true, false, 1, 0, on, off, enable, disable enabled: true or enabled: 1
int Numeric strings or integers timeout_sec: 60 or timeout_sec: "60"
CSV string Comma-separated values (parsed as list internally) allowed_user_ids: "123,456,789"

Note: The minimal YAML parser does not support native arrays. Use CSV strings for list fields like allowed_user_ids.


Goal

Consolidate developer-specific configuration (handsoff, supervisor, Telegram, server runtime) into .agentize.local.yaml, making docs YAML-first while preserving env-var overrides for compatibility.

Success criteria:

  • .agentize.local.yaml supports handsoff, supervisor, and Telegram settings used by hooks and server, with env vars still overriding.
  • docs/envvar.md is updated to YAML-first guidance with a clear mapping and explicit env-only exceptions.
  • Existing behavior remains unchanged when .agentize.local.yaml is absent.

Out of scope:

  • Shell script migration for HANDSOFF_*, TG_*, WT_*, or TEST_*.
  • YAML-based permission rule definitions (remain Python).
  • Deep-merge semantics, array support, or external YAML dependencies.
  • New CLI commands like lol config show/validate.

Codebase Analysis

File changes:

File Level Purpose
docs/envvar.md major Rewrite to YAML-first config reference with env override mapping
docs/architecture/metadata.md minor Clarify .agentize.local.yaml scope and precedence
docs/feat/core/handsoff.md medium Add YAML config section and precedence notes
docs/feat/permissions/telegram.md medium Add YAML config section and precedence notes
docs/feat/server.md minor Align runtime config section with YAML-first guidance
python/agentize/server/runtime_config.md medium Update schema to include handsoff and notes on CSV fields
python/agentize/server/README.md minor Align server config guidance with YAML-first approach
.claude-plugin/hooks/pre-tool-use.md medium Replace env-var-only guidance with YAML-first mapping
.claude-plugin/hooks/README.md minor Note YAML-based config for hooks
.claude-plugin/lib/README.md minor Document new local config helper
.claude-plugin/lib/session_utils.md medium Update handsoff enablement to YAML-first
.claude-plugin/lib/permission/README.md minor Add YAML config mention for Telegram
.claude-plugin/lib/local_config.md (new) major Document new local config interface (Est: 80 LOC)
.agentize.local.example.yaml (new) major Provide YAML template for developer setup (Est: 50 LOC)
python/tests/README.md minor Add new test file to test matrix
python/agentize/server/runtime_config.py medium Accept handsoff and metadata keys; add coercion helpers
python/agentize/server/__main__.py medium Apply .agentize.local.yaml for server defaults
python/agentize/server/github.py medium Use config-aware HANDSOFF_DEBUG helper
.claude-plugin/lib/local_config.py (new) major Load YAML, cache, and resolve env override
.claude-plugin/lib/session_utils.py medium Read handsoff enabled from YAML with env override
.claude-plugin/lib/logger.py minor Read handsoff.debug from YAML with env override
.claude-plugin/lib/permission/determine.py medium Read Telegram and auto-permission from YAML
.claude-plugin/lib/workflow.py medium Read supervisor config from YAML
.claude-plugin/hooks/stop.py minor Read max continuations from YAML
python/tests/test_runtime_config.py medium Add handsoff schema and coercion tests
python/tests/test_local_config.py (new) major Tests for YAML lookup and env override (Est: 120 LOC)

Interface Design

New interfaces:

  • lib/local_config.py
    • load_local_config(start_dir: Path | None = None) -> tuple[dict, Path | None] — Parse .agentize.local.yaml by walking parent directories.
    • get_local_value(path: str, env: str | None, default: Any, coerce: Callable | None = None) -> Any — Resolve YAML value by dotted path, apply env override, optional coercion.
    • coerce_bool(value: Any, default: bool) -> bool — Accept 1/0/true/false/on/off/enable/disable.
    • coerce_int(value: Any, default: int) -> int — Handle numeric strings and ints.
    • coerce_csv_ints(value: Any) -> list[int] — Parse comma-separated user IDs.

Modified interfaces:

  • python/agentize/server/runtime_config.py top-level key whitelist expands:
- VALID_TOP_LEVEL_KEYS = {"server", "telegram", "workflows"}
+ VALID_TOP_LEVEL_KEYS = {"server", "telegram", "workflows", "handsoff", "project", "git", "agentize", "worktree", "pre_commit"}

Test Strategy

Test modifications:

  • python/tests/test_runtime_config.py — add cases for handsoff section parsing and key whitelist expansion.
  • python/tests/test_runtime_config.py — add tests for boolean coercion helper behavior.
  • python/tests/test_local_config.py (new) — YAML lookup by dotted path and env override precedence.
  • python/tests/test_local_config.py (new) — CSV parsing for telegram.allowed_user_ids.

Test data required:

  • Temporary .agentize.local.yaml fixtures with handsoff, telegram, and server sections.
  • CSV string fixture for telegram.allowed_user_ids.

Implementation Steps

Step 1: Update documentation and sample config (Estimated: 260 LOC)

  • Update all docs to YAML-first guidance
  • Create .agentize.local.example.yaml template
  • Dependencies: None

Step 2: Add/extend tests for YAML config and precedence (Estimated: 200 LOC)

  • Add tests for handsoff parsing and key whitelist
  • Create test_local_config.py for YAML lookup, env override, and CSV parsing
  • Dependencies: Step 1

Step 3: Extend runtime config parser (Estimated: 90 LOC)

  • Expand VALID_TOP_LEVEL_KEYS to include handsoff and metadata keys
  • Add coercion helpers (bool/int/CSV)
  • Dependencies: Step 2

Step 4: Add local config helper and wire Claude plugin modules (Estimated: 260 LOC)

  • Create .claude-plugin/lib/local_config.py with YAML loader, caching, env override
  • Update session_utils.py, logger.py, determine.py, workflow.py, stop.py
  • Dependencies: Step 2

Step 5: Wire server entrypoint to YAML defaults (Estimated: 140 LOC)

  • Update __main__.py to load .agentize.local.yaml and apply defaults
  • Update github.py to use config-aware HANDSOFF_DEBUG helper
  • Dependencies: Step 2

Total estimated complexity: 950 LOC (Large)
Recommended approach: Milestone commits

Milestone strategy:

  • M1: Complete docs + example file + tests (Steps 1–2)
  • M2: Runtime config and local_config implementation (Steps 3–4)
  • Delivery: Server wiring + test pass (Step 5)

Risks and Mitigations

Risk Likelihood Impact Mitigation
Minimal YAML parser lacks arrays H M Document CSV for allowed_user_ids; keep parser minimal
Scope creep into shell scripts M H Explicitly keep shell env vars out of scope and documented as env-only
Backward compatibility regressions M H Env overrides remain, add tests for precedence
Duplicate config loaders M M Keep local_config small and documented; consider consolidation later

Dependencies

No new external dependencies; continue using the existing minimal YAML parser.

Related PR

TBD - will be updated when PR is created

Metadata

Metadata

Assignees

No one assigned

    Labels

    agentize:planPlan created by /ultra-planner command

    Type

    No type

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions