#[allow(clippy::wildcard_imports)]
use super::*;
use std::collections::BTreeMap;
pub(super) fn build(sections: &mut BTreeMap<String, SectionSchema>) {
let cfg = crate::core::config::Config::default();
let mut autonomy = BTreeMap::new();
autonomy.insert(
"enabled".into(),
key(
"bool",
serde_json::json!(cfg.autonomy.enabled),
"Enable autonomous background behaviors",
),
);
autonomy.insert(
"auto_consolidate".into(),
key(
"bool",
serde_json::json!(cfg.autonomy.auto_consolidate),
"Auto-consolidate knowledge periodically",
),
);
autonomy.insert(
"consolidate_every_calls".into(),
key(
"u32",
serde_json::json!(cfg.autonomy.consolidate_every_calls),
"Consolidate knowledge every N tool calls",
),
);
autonomy.insert(
"consolidate_cooldown_secs".into(),
key(
"u64",
serde_json::json!(cfg.autonomy.consolidate_cooldown_secs),
"Minimum seconds between consolidation runs",
),
);
autonomy.insert(
"cognition_loop_enabled".into(),
key_with_env(
"bool",
serde_json::json!(cfg.autonomy.cognition_loop_enabled),
"No effect (kept so existing configs load): the cognition loop is retired; scheduled consolidation runs the memory lifecycle",
"LEAN_CTX_COGNITION_LOOP_ENABLED",
),
);
autonomy.insert(
"cognition_loop_interval_secs".into(),
key_with_env(
"u64",
serde_json::json!(cfg.autonomy.cognition_loop_interval_secs),
"No effect (kept so existing configs load): the cognition loop is retired",
"LEAN_CTX_COGNITION_LOOP_INTERVAL_SECS",
),
);
autonomy.insert(
"cognition_loop_max_steps".into(),
key_with_env(
"u8",
serde_json::json!(cfg.autonomy.cognition_loop_max_steps),
"No effect (kept so existing configs load): the cognition loop is retired",
"LEAN_CTX_COGNITION_LOOP_MAX_STEPS",
),
);
autonomy.insert(
"cognition_synthesis_min_cluster".into(),
key_with_env(
"usize",
serde_json::json!(cfg.autonomy.cognition_synthesis_min_cluster),
"No effect (kept so existing configs load): observation synthesis is retired with the cognition loop",
"LEAN_CTX_COGNITION_SYNTHESIS_MIN_CLUSTER",
),
);
sections.insert(
"autonomy".into(),
SectionSchema {
description: "Controls autonomous background behaviors (preload, dedup, consolidation)"
.into(),
keys: autonomy,
},
);
let mut providers = BTreeMap::new();
providers.insert(
"enabled".into(),
key(
"bool",
serde_json::json!(cfg.providers.enabled),
"Master switch for the provider subsystem (GitHub, GitLab, etc.)",
),
);
providers.insert(
"auto_index".into(),
key(
"bool",
serde_json::json!(cfg.providers.auto_index),
"Auto-ingest provider results into BM25/embedding indexes",
),
);
providers.insert(
"cache_ttl_secs".into(),
key(
"u64",
serde_json::json!(cfg.providers.cache_ttl_secs),
"Default cache TTL for provider results (seconds)",
),
);
providers.insert(
"github.enabled".into(),
key(
"bool",
serde_json::json!(cfg.providers.github.enabled),
"Enable/disable GitHub provider",
),
);
providers.insert(
"github.api_url".into(),
key(
"string",
serde_json::json!(cfg.providers.github.api_url),
"GitHub API base URL (for GitHub Enterprise)",
),
);
providers.insert(
"gitlab.enabled".into(),
key(
"bool",
serde_json::json!(cfg.providers.gitlab.enabled),
"Enable/disable GitLab provider",
),
);
providers.insert(
"gitlab.api_url".into(),
key(
"string",
serde_json::json!(cfg.providers.gitlab.api_url),
"GitLab API base URL (for self-hosted instances)",
),
);
providers.insert(
"mcp_bridges.<name>.url".into(),
key(
"string",
serde_json::json!(null),
"HTTP/SSE URL for a remote MCP server",
),
);
providers.insert(
"mcp_bridges.<name>.command".into(),
key(
"string",
serde_json::json!(null),
"Command to spawn a local MCP server (stdio transport)",
),
);
providers.insert(
"mcp_bridges.<name>.args".into(),
key(
"array",
serde_json::json!([]),
"Arguments for the MCP server command",
),
);
providers.insert(
"mcp_bridges.<name>.auth_env".into(),
key(
"string",
serde_json::json!(null),
"Environment variable name containing auth token for MCP server",
),
);
sections.insert(
"providers".into(),
SectionSchema {
description:
"External context providers (GitHub, GitLab, Jira, MCP bridges, etc.). Set tokens via env vars (GITHUB_TOKEN, GITLAB_TOKEN). MCP bridges connect external MCP servers as context sources."
.into(),
keys: providers,
},
);
let mut loop_det = BTreeMap::new();
loop_det.insert(
"normal_threshold".into(),
key(
"u32",
serde_json::json!(cfg.loop_detection.normal_threshold),
"Repetitions before reducing output",
),
);
loop_det.insert(
"reduced_threshold".into(),
key(
"u32",
serde_json::json!(cfg.loop_detection.reduced_threshold),
"Repetitions before further reducing output",
),
);
loop_det.insert(
"blocked_threshold".into(),
key(
"u32",
serde_json::json!(cfg.loop_detection.blocked_threshold),
"Repetitions before blocking. 0 = disabled",
),
);
loop_det.insert(
"window_secs".into(),
key(
"u64",
serde_json::json!(cfg.loop_detection.window_secs),
"Time window in seconds for loop detection",
),
);
loop_det.insert(
"search_group_limit".into(),
key(
"u32",
serde_json::json!(cfg.loop_detection.search_group_limit),
"Maximum unique searches within a loop window",
),
);
loop_det.insert(
"tool_total_limits".into(),
key(
"table",
serde_json::json!({"ctx_read": 100, "ctx_search": 80, "ctx_shell": 50}),
"Per-tool total call limits within a session. Keys are tool names, values are max calls",
),
);
sections.insert(
"loop_detection".into(),
SectionSchema {
description: "Loop detection settings for preventing repeated identical tool calls"
.into(),
keys: loop_det,
},
);
let mut updates = BTreeMap::new();
updates.insert(
"auto_update".into(),
key(
"bool",
serde_json::json!(cfg.updates.auto_update),
"Enable automatic updates (requires explicit opt-in)",
),
);
updates.insert(
"check_interval_hours".into(),
key(
"u64",
serde_json::json!(cfg.updates.check_interval_hours),
"How often to check for updates (hours)",
),
);
updates.insert(
"notify_only".into(),
key(
"bool",
serde_json::json!(cfg.updates.notify_only),
"Only notify about updates, don't install automatically",
),
);
updates.insert(
"pinned_version".into(),
key(
"string|null",
serde_json::json!(cfg.updates.pinned_version),
"Release version used by update commands and scheduled updates; unset follows latest",
),
);
sections.insert(
"updates".into(),
SectionSchema {
description: "Automatic update configuration".into(),
keys: updates,
},
);
let mut context = BTreeMap::new();
context.insert(
"budget_tokens".into(),
key_with_env(
"usize",
serde_json::json!(cfg.context.budget_tokens),
"Fixed per-session context budget (tool schemas + MCP instructions + auto-loaded rules + wakeup briefing). `doctor overhead` warns past this; `doctor overhead --gate` exits non-zero for CI. 0 disables the warning",
"LEAN_CTX_CONTEXT_BUDGET_TOKENS",
),
);
context.insert(
"proactive_expansion".into(),
key(
"bool",
serde_json::json!(cfg.context.proactive_expansion),
"Inject relevant CCR archives into later tool responses",
),
);
context.insert(
"proactive_expansion_budget_tokens".into(),
key(
"usize",
serde_json::json!(cfg.context.proactive_expansion_budget_tokens),
"Maximum proactive archive tokens per tool response",
),
);
context.insert(
"proactive_expansion_threshold".into(),
key(
"f64",
serde_json::json!(cfg.context.proactive_expansion_threshold),
"Minimum normalized BM25 score for proactive expansion",
),
);
context.insert(
"proactive_expansion_max_age_secs".into(),
key(
"u64",
serde_json::json!(cfg.context.proactive_expansion_max_age_secs),
"Maximum age of CCR content eligible for proactive expansion",
),
);
sections.insert(
"context".into(),
SectionSchema {
description: "Fixed-context budget accounting (#964)".into(),
keys: context,
},
);
let mut boundary = BTreeMap::new();
boundary.insert(
"cross_project_search".into(),
key(
"bool",
serde_json::json!(cfg.boundary_policy.cross_project_search),
"Allow searching across project boundaries",
),
);
boundary.insert(
"cross_project_import".into(),
key(
"bool",
serde_json::json!(cfg.boundary_policy.cross_project_import),
"Allow importing knowledge from other projects",
),
);
boundary.insert(
"universal_gotchas_enabled".into(),
key(
"bool",
serde_json::json!(cfg.boundary_policy.universal_gotchas_enabled),
"Load universal (cross-project) gotchas",
),
);
sections.insert(
"boundary_policy".into(),
SectionSchema {
description: "Cross-project boundary and access control policies".into(),
keys: boundary,
},
);
let mut secret_det = BTreeMap::new();
secret_det.insert(
"enabled".into(),
key(
"bool",
serde_json::json!(cfg.secret_detection.enabled),
"Enable secret/credential detection in tool outputs",
),
);
secret_det.insert(
"redact".into(),
key(
"bool",
serde_json::json!(cfg.secret_detection.redact),
"Redact detected secrets from output",
),
);
secret_det.insert(
"custom_patterns".into(),
key(
"array",
serde_json::json!(cfg.secret_detection.custom_patterns),
"Additional regex patterns to detect as secrets",
),
);
secret_det.insert(
"exclude_patterns".into(),
key(
"array",
serde_json::json!(cfg.secret_detection.exclude_patterns),
"Subtractive allowlist: matches covered by these regexes are never reported or redacted (#718)",
),
);
sections.insert(
"secret_detection".into(),
SectionSchema {
description: "Secret/credential detection and redaction settings".into(),
keys: secret_det,
},
);
let mut sensitivity = BTreeMap::new();
sensitivity.insert(
"enabled".into(),
key(
"bool",
serde_json::json!(cfg.sensitivity.enabled),
"Enable the per-item sensitivity policy floor (no-op when false)",
),
);
sensitivity.insert(
"policy_floor".into(),
key(
"string",
serde_json::json!(cfg.sensitivity.policy_floor.as_str()),
"Block items at/above this level: public|internal|confidential|secret",
),
);
sensitivity.insert(
"action".into(),
key(
"string",
serde_json::json!(cfg.sensitivity.action.as_str()),
"How to enforce the floor: redact (mask spans) or drop (withhold item)",
),
);
sections.insert(
"sensitivity".into(),
SectionSchema {
description: "Per-item sensitivity model with a uniform policy floor (#212)".into(),
keys: sensitivity,
},
);
let gw = &cfg.context_gateway;
let mut context_gateway = BTreeMap::new();
context_gateway.insert(
"enabled".into(),
key_with_env(
"bool",
serde_json::json!(gw.enabled),
"Run context admission on every governed source before caching or compression",
"LEAN_CTX_CONTEXT_GATEWAY",
),
);
let mode = serde_json::to_value(gw.mode)
.ok()
.and_then(|v| v.as_str().map(str::to_owned))
.unwrap_or_default();
context_gateway.insert(
"mode".into(),
key_enum(
&["developer", "governed", "sovereign"],
&mode,
"Governed and sovereign treat unclassified content as internal, never public",
),
);
for (name, action, what) in [
(
"secrets",
gw.secrets,
"credentials and keys (patterns from [secret_detection])",
),
("pii", gw.pii, "checksum-validated PII: payment cards"),
(
"injection",
gw.injection,
"prompt-injection heuristic (OWASP LLM01)",
),
(
"classification",
gw.classification,
"explicit markings such as CONFIDENTIAL (redact = withhold)",
),
] {
context_gateway.insert(
name.into(),
key_enum(
&["off", "warn", "redact", "block"],
action.as_str(),
&format!("Action for {what}"),
),
);
}
context_gateway.insert(
"max_inspected_bytes".into(),
key(
"usize",
serde_json::json!(gw.max_inspected_bytes),
"Bytes each detector inspects per object; beyond it coverage is partial (withheld in governed/sovereign mode)",
),
);
context_gateway.insert(
"detector_timeout_ms".into(),
key(
"u64",
serde_json::json!(gw.detector_timeout_ms),
"Per-detector time budget; a detector that runs out reports timed_out, never clean",
),
);
context_gateway.insert(
"destination_constraints".into(),
key(
"array",
serde_json::to_value(&gw.destination_constraints).unwrap_or_default(),
"Hard final-egress placement constraints: local_only and organization_private; independent of content scanning",
),
);
context_gateway.insert(
"allowed_destinations".into(),
key(
"array",
serde_json::to_value(&gw.allowed_destinations).unwrap_or_default(),
"Optional target allow-list using DestinationV1 fields; provider/locality are required, and configured account_ref/region must be bound by trusted route metadata",
),
);
context_gateway.insert(
"hud".into(),
key_enum(
&["auto", "in_band", "status_line"],
gw.hud.as_str(),
"Where redaction counts appear: auto keeps them out of the model's context when the Claude Code status line shows lean-ctx",
),
);
sections.insert(
"context_gateway".into(),
SectionSchema {
description:
"Context Gateway admission: built-in detectors, on by default, global-only".into(),
keys: context_gateway,
},
);
let mut gateway = BTreeMap::new();
gateway.insert(
"enabled".into(),
key(
"bool",
serde_json::json!(cfg.gateway.enabled),
"Enable the MCP Tool-Catalog Gateway (no-op when false)",
),
);
gateway.insert(
"top_n".into(),
key(
"integer",
serde_json::json!(cfg.gateway.top_n),
"How many tools `ctx_tools find` returns per query (clamped 1..=50)",
),
);
gateway.insert(
"cache_ttl_secs".into(),
key(
"integer",
serde_json::json!(cfg.gateway.cache_ttl_secs),
"Aggregated-catalog cache lifetime in seconds",
),
);
gateway.insert(
"call_timeout_secs".into(),
key(
"integer",
serde_json::json!(cfg.gateway.call_timeout_secs),
"Per-operation timeout for downstream connect/list/call (seconds)",
),
);
sections.insert(
"gateway".into(),
SectionSchema {
description: "MCP Tool-Catalog Gateway: aggregate + query-route downstream MCP servers (#210). Global-only.".into(),
keys: gateway,
},
);
let mut gateway_servers = BTreeMap::new();
gateway_servers.insert(
"name".into(),
key(
"string",
serde_json::json!(""),
"Stable server id; becomes the catalog namespace (`name::tool`)",
),
);
gateway_servers.insert(
"transport".into(),
key(
"string",
serde_json::json!("stdio"),
"Transport: stdio (spawn command) or http (connect to url)",
),
);
gateway_servers.insert(
"enabled".into(),
key(
"bool",
serde_json::json!(true),
"Per-server switch (default true)",
),
);
gateway_servers.insert(
"command".into(),
key(
"string",
serde_json::json!(""),
"Executable to spawn (stdio transport)",
),
);
gateway_servers.insert(
"args".into(),
key(
"array",
serde_json::json!([]),
"Arguments for the spawned command (stdio transport)",
),
);
gateway_servers.insert(
"env".into(),
key(
"table",
serde_json::json!({}),
"Extra environment variables for the child process (stdio transport)",
),
);
gateway_servers.insert(
"secret_env".into(),
key(
"table",
serde_json::json!({}),
"Secret environment variables mapped to memento references (stdio transport)",
),
);
gateway_servers.insert(
"url".into(),
key(
"string",
serde_json::json!(""),
"Streamable-HTTP endpoint (http transport)",
),
);
gateway_servers.insert(
"headers".into(),
key(
"table",
serde_json::json!({}),
"Extra request headers, e.g. Authorization (http transport)",
),
);
gateway_servers.insert(
"secret_headers".into(),
key(
"table",
serde_json::json!({}),
"Secret HTTP headers mapped to memento references (http transport)",
),
);
sections.insert(
"gateway.servers".into(),
SectionSchema {
description: "Downstream MCP servers (array of tables: `[[gateway.servers]]`)".into(),
keys: gateway_servers,
},
);
let mut mcp_servers = BTreeMap::new();
mcp_servers.insert(
"id".into(),
key(
"string",
serde_json::json!(""),
"Registry id; becomes the governed route `/mcp/{id}` on the enterprise gateway server's port, not the local proxy (lowercase alnum/-/_)",
),
);
mcp_servers.insert(
"url".into(),
key(
"string",
serde_json::json!(""),
"Upstream Streamable-HTTP endpoint (HTTPS; loopback HTTP ok; plain HTTP needs [proxy] allow_insecure_http_upstream)",
),
);
mcp_servers.insert(
"auth_env".into(),
key(
"string",
serde_json::json!(""),
"Env var holding the upstream credential the gateway injects as `Authorization: Bearer <env value>` (callers never see it)",
),
);
mcp_servers.insert(
"enabled".into(),
key(
"bool",
serde_json::json!(true),
"Research-only per-server switch (default true once a development configuration explicitly adds this server)",
),
);
sections.insert(
"gateway_server.mcp_servers".into(),
SectionSchema {
description: "Research-only organization gateway registry (array of tables: `[[gateway_server.mcp_servers]]`). It is inactive in the public local Runtime and must be explicitly configured for development evaluation.".into(),
keys: mcp_servers,
},
);
let mut telemetry = BTreeMap::new();
telemetry.insert(
"enabled".into(),
key(
"bool",
serde_json::json!(cfg.telemetry.enabled),
"Enable anonymous product telemetry (cumulative daily aggregates — no code, paths or PII)",
),
);
telemetry.insert(
"preference".into(),
key_enum(
&["default_on", "explicitly_enabled", "explicitly_disabled"],
"default_on",
"Whether telemetry is using the v4 default or an explicit user choice",
),
);
sections.insert(
"telemetry".into(),
SectionSchema {
description: "Privacy-safe default-on telemetry with explicit opt-out".into(),
keys: telemetry,
},
);
let vd = &cfg.value_display;
let mut value_display = BTreeMap::new();
value_display.insert(
"mode".into(),
key_enum_with_env(
&["off", "minimal", "milestones", "verbose"],
"minimal",
"How lean-ctx shows its measured value to you (never to the model): off, minimal (default: status line + threshold recaps), milestones (+ OS notifications), verbose (every recap window)",
"LEAN_CTX_VALUE_DISPLAY",
),
);
value_display.insert(
"recap_every_turns".into(),
key(
"u32",
serde_json::json!(vd.recap_every_turns),
"A turn recap is considered every N agent turns (default 5)",
),
);
value_display.insert(
"recap_min_tokens".into(),
key(
"u64",
serde_json::json!(vd.recap_min_tokens),
"Show a turn recap only when the window saved at least this many tokens or a security event happened (default 10000)",
),
);
value_display.insert(
"notifications".into(),
key(
"bool",
serde_json::json!(vd.notifications),
"OS notifications for milestones in mode milestones or verbose, at most one per day (default true)",
),
);
value_display.insert(
"git_trailer".into(),
key(
"bool",
serde_json::json!(vd.git_trailer),
"Opt-in `lean-ctx:` trailer in commit messages (default false)",
),
);
sections.insert(
"value_display".into(),
SectionSchema {
description: "User-only value surface: status line, recaps, prompt segment, milestones. Every number is backed by `lean-ctx value`.".into(),
keys: value_display,
},
);
}