Skip to content

Commit 0ff8914

Browse files
aksOpsclaude
andauthored
refactor: framework de-coupling — generic runtime, ASR as use case (v1.1) (#2)
* feat(05): configurable session-id prefix (DECOUPLE-01) Replace hardcoded INC-YYYYMMDD-NNN session-id format with config-driven prefix. Apps declare their prefix via FrameworkAppConfig.session_id_prefix (default "SES", validated 1-16 chars, alphanumeric + hyphens). The prefix threads through SessionStore -> Session.id_format -> repository. Configs: - incident_management.yaml: INC - code_review.runtime.yaml: REVIEW - config.yaml (default): SES (also explicitly INC for asr-default) Validation rejects empty / whitespace / symbols / underscore / >16-chars at config-load time via field_validator. First step toward v1.1 framework de-coupling — runtime no longer hardcodes the incident-management session-id convention. Tests: 869 passed, 3 skipped (16 new parametrized tests in tests/test_session_id_prefix.py covering default, custom prefixes INC/REVIEW/HR/MY-APP/16-char, and invalid inputs). dist/* bundles regenerated. Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com> * feat(06-01): add TerminalToolRule + StatusDef pydantic models New module src/runtime/terminal_tools.py introduces the type system for the generic terminal-tool registry that replaces the hardcoded _TERMINAL_TOOL_RULES table in orchestrator.py and the _TYPED_TERMINAL_TOOLS frozenset in graph.py (consumed in Wave 2). - TerminalToolRule: tool_name + status + extract_fields dict, all extra="forbid" to reject typos in app YAML at boot (D-06-01). - extract_fields generalises the v1.0 _extract_team(team_keys) positional-tuple lookup into a {dest: [args.X|result.X]} dict so apps name fields whatever fits (team, severity, reviewer) (D-06-02). - StatusDef: name + terminal + kind (Literal of 5 categories), no color or label (UI presentation owned by UIConfig.badges per D-06-05 rejected alternative). The new module is vocabulary-free: zero references to mark_resolved/mark_escalated/notify_oncall/etc, satisfying DECOUPLE-02 for the framework side. * feat(06-01): register terminal-tool registry on OrchestratorConfig Add three new fields and a cross-field model_validator to OrchestratorConfig so apps can declare their terminal-tool rules and status vocabulary in YAML without writing Python (D-06-03). Fields: - terminal_tools: list[TerminalToolRule] (D-06-01) - statuses: dict[str, StatusDef] (D-06-05) - default_terminal_status: str | None (D-06-06) Cross-field invariants enforced at config-load (raises pydantic.ValidationError): - statuses non-empty => default_terminal_status required - default_terminal_status must reference a declared status - referenced default status must have terminal=True - every terminal_tools[i].status must reference a declared status - empty statuses with non-empty terminal_tools or set default_terminal_status is rejected (config mistake) Bare OrchestratorConfig() still constructs with empty registry — the framework default for unconfigured apps stays valid. Also tightens model_config = {"extra": "forbid"} on OrchestratorConfig so unknown YAML keys fail loudly at boot, matching the extra="forbid" pattern used by sibling configs in this file. Wave 2 (06-02) will consume these fields in _finalize_session_status / _harvest_typed_terminal and ship the same-PR incident_management.yaml registration (D-06-04). * test(06-01): add 16 boundary tests for terminal-tool registry tests/test_status_vocabulary.py exercises the new OrchestratorConfig.{terminal_tools,statuses,default_terminal_status} plus the TerminalToolRule / StatusDef pydantic models. Covers DECOUPLE-03 acceptance: framework rejects unknown statuses and unknown defaults at config-load, not at gateway-eval time. Test coverage: - TerminalToolRule shape: minimal + extract_fields round-trip + extra="forbid" rejects unknown keys (D-06-01, D-06-02) - StatusDef shape: minimal + Literal kind enforcement + extra="forbid" rejects color (D-06-05 — UI presentation leak) - OrchestratorConfig happy path with mixed rules and statuses - bare OrchestratorConfig() still constructs (framework default invariant) - default_terminal_status without statuses rejected - terminal_tools without statuses rejected - default required when statuses non-empty - default must reference a declared status (error names valid keys) - default must be terminal=True (in_progress fails) - terminal_tools[i].status must reference a declared status (error names the index and the offending value, both for index 0 and later indices) - extra="forbid" on OrchestratorConfig survives the new fields All 16 tests pass; full suite at 885 passed (was 869). * test(06-02): add concept-leak ratchet (DECOUPLE-06) — RED Two new test files; both fail until Tasks 2.2/2.3/2.4 land: - tests/test_concept_leak_ratchet.py — DECOUPLE-06 binary ratchet walking src/runtime/ for the 6 forbidden tokens (mark_resolved, mark_escalated, notify_oncall, submit_hypothesis, update_incident, apply_fix). Currently fails because _TERMINAL_TOOL_RULES still lives in orchestrator.py and _TYPED_TERMINAL_TOOLS in graph.py. - tests/test_terminal_tool_registry.py — 9 finalize-path integration tests covering both incident_management and a synthetic code_review-style registration. Currently fails because Orchestrator._infer_terminal_decision / _extract_field are not yet instance methods (D-06-08). RED phase commit per the v1.1 milestone TDD discipline. GREEN follows in the atomic framework-migration commit. * checkpoint: pre-yolo 2026-05-06T17:32:43 * feat(06-02): generic terminal-tool registry + status vocab + parameterized escalate (DECOUPLE-02, DECOUPLE-03, DECOUPLE-06) Migrates `_finalize_session_status` and the typed-terminal harvester to read app-registered rules off `OrchestratorConfig` instead of the v1.0 hardcoded `_TERMINAL_TOOL_RULES` / `_TYPED_TERMINAL_TOOLS` tables. Ships the framework refactor and the matching incident_management YAML registration in a single atomic commit per D-06-04. Decisions cited: - D-06-02: extract_fields lookup syntax preserved (args.X / result.X). - D-06-03: registry lives in app YAML; pydantic-validated at boot. - D-06-04: same-PR atomic commit — incident_management end-to-end flow stays green throughout. - D-06-05: StatusDef.kind drives the escalated_to mirror dispatch rather than hardcoded vocabulary. - D-06-06: default_terminal_status is app-owned (incident_management uses needs_review; code_review uses unreviewed). - D-06-08: `_finalize_session_status` reads `self.cfg.orchestrator` directly via instance method shape. Resolutions applied: - Resolution A (escalate path) — Option B: parameterised on the new `OrchestratorConfig.escalate_action_tool_name` and `escalate_action_default_team` fields. The orchestrator's `resume_session(action="escalate")` looks up the matching rule in `terminal_tools`, drives status assignment from `rule.status`, and pulls extra-field destination key from `rule.extract_fields`. Hardcoded `notify_oncall` / `platform-oncall` / `escalated_to` literals removed from `orchestrator.py`. - Resolution B (ratchet scope) — Option 3: `RATCHET_ALLOWLIST` carries 8 entries with phase-handles (`Phase 7` for `mcp_servers/remediation.py`; `Phase 8` for `terminal_tools.py` docstrings + `storage/event_log.py` docstrings). Companion meta-test asserts each entry still matches a real line — when Phase 7 / 8 ship and the offending file is gone, the meta-test fails and forces stale-entry deletion. Allowlist is shrink-only. New OrchestratorConfig fields: - `patch_tools: list[str]` — tools whose `args.patch` blob the harvester folds into agent confidence/signal/rationale. Avoids a third hardcoded `update_incident` leak by routing the recognition through YAML. - `harvest_terminal_tools: list[str]` — tools the harvester treats as typed-terminal for confidence capture but the orchestrator's finalize path does NOT transition status on. Used for `submit_hypothesis`-style stage-complete signals. - `escalate_action_tool_name: str | None` — MCP tool the orchestrator invokes when a user clicks Escalate. None means no side-effect. - `escalate_action_default_team: str | None` — fallback team when the user did not pick one. Same-PR YAML registration shipped in: - config/incident_management.yaml - config/config.yaml - config/config.yaml.example - config/code_review.runtime.yaml (placeholder, full migration in Phase 8 per D-06-04) Test fixtures updated to provide `cfg` to the finalize-only stub orchestrators (test_finalize_status_inference.py, test_session_lock.py, test_finalize_concurrent.py) and to pass `terminal_tool_names` / `patch_tool_names` to `_harvest_tool_calls_and_patches` / `make_agent_node` (test_harvester_typed.py, test_agent_node.py, test_gate.py). The `cfg` fixture in test_resume.py now mirrors the incident_management YAML registration so the parameterised escalate path flows through unchanged behaviour for legacy callers. Verification: - pytest: 899 passed (vs 870 baseline pre-Phase 6). - ratchet `git grep` — every match is allowlisted and explained. - ruff: clean on every touched file. - All three production YAMLs (config.yaml, config.yaml.example, code_review.runtime.yaml) validate against the new OrchestratorConfig schema. dist/ bundle regen deliberately deferred to Plan 06-03. * build(06-03): regenerate dist/* bundles + STATE.md update for Phase 6 close-out Phase 6 close-out (Generic Terminal-Tool Registry + Status Vocabulary). Final wave: regenerated dist/app.py, dist/apps/incident-management.py, dist/apps/code-review.py from the post-Phase-6 src/runtime/ + examples/ tree. dist/ui.py regenerated to byte-identical content (config-driven UI shell already generic; no v1.1 surface changes). The bundles now embed TerminalToolRule + StatusDef pydantic models, drop the v1.0 _TERMINAL_TOOL_RULES table and _TYPED_TERMINAL_TOOLS frozenset, and carry per-rule extract_fields generalising the v1.0 _extract_team lookup. Phase 6 final acceptance: - git grep leak-ratchet: 9 matches, all allowlisted (2 Phase 7, 7 Phase 8) per RATCHET_ALLOWLIST in test_concept_leak_ratchet.py - pytest -q: 899 passed (Phase 5 baseline 873; +28 from Phase 6: +14 status_vocabulary, +9 terminal_tool_registry, +5 ratchet) - bundle smoke tests: 12 passed - ruff src/runtime/ tests/ examples/: clean Phase 6 totals: 3 plans, 11 tasks, 6 commits across 3 waves (8a249f3, 700b4e1, d7e8b26, 1b43017, 070e15d, this commit). Closes DECOUPLE-02, DECOUPLE-03, DECOUPLE-06. STATE.md and 06-03-SUMMARY.md document the full hand-off to Phase 7 (DECOUPLE-04) + Phase 8 (DECOUPLE-05 + 07) — both files are gitignored per project policy and NOT included in this commit. Ready for /gsd-transition to Phase 7. * checkpoint: pre-yolo 2026-05-07T01:00:50 * checkpoint: pre-yolo 2026-05-07T01:10:11 * feat(07-01): relocate app MCP servers to examples/ + configurable discovery (DECOUPLE-04) Phase 7 / DECOUPLE-04 — single atomic commit per D-07-04. Framework no longer hardcodes incident-vocabulary MCP servers; apps declare their per-tool servers via a generic configurable dotted-path list. * D-07-01 — moved (git mv, history preserved): src/runtime/mcp_servers/{observability,remediation,user_context,__init__}.py -> examples/incident_management/mcp_servers/ * D-07-02 — new field OrchestratorConfig.mcp_servers: list[str] (src/runtime/config.py). YAML wiring: config/config.yaml + config/incident_management.yaml + config.yaml.example declare orchestrator.mcp_servers with the 3 dotted paths under examples.incident_management.mcp_servers.* config/code_review.runtime.yaml declares orchestrator.mcp_servers: [] — empty-list branch verified clean (dist/apps/code-review.py contains zero examples.incident_management.mcp_servers refs). * D-07-03 — converted setters to register(mcp_app, cfg) contract: observability.set_environments -> register(mcp_app, cfg) closing over cfg.environments via _make_environment_validator (snapshotted; no module-level mutable list). remediation.set_escalation_teams -> register(mcp_app, cfg) closing over cfg.framework.escalation_teams (or cfg.escalation_teams fallback) into a module-level snapshot tuple. user_context -> no-op register(mcp_app, cfg) for contract uniformity. Chosen contract: two-arg register(mcp_app, cfg). The orchestrator passes mcp_app=None — modules expose their own per-module FastMCP instance composed by the loader; mcp_app exists for future composition needs. * orchestrator.py:357-365 (now ~365-380): replaced the silent-swallow try/except setter calls with a single importlib-driven loop: for module_path in cfg.orchestrator.mcp_servers: mod = importlib.import_module(module_path) reg = getattr(mod, "register", None) if reg is None: raise RuntimeError( f"orchestrator.mcp_servers entry {module_path!r} does " f"not expose a `register(mcp_app, cfg)` callable" ) reg(None, cfg) No more silent except — missing register raises explicitly per the must_haves contract. * Bundler patched: scripts/build_single_file.py RUNTIME_MODULE_ORDER:66-68 (the three hardcoded mcp_servers paths) removed from the framework-only block; the equivalent entries added to INCIDENT_APP_MODULE_ORDER (under EXAMPLES_ROOT) so the per-tool modules ship in dist/apps/incident-management.py without leaking into dist/app.py (framework-only) or dist/apps/code-review.py. * Bundles regenerated: dist/app.py, dist/ui.py, dist/apps/incident-management.py, dist/apps/code-review.py. * Import-site flip: ~60 sites across 19 test files + 4 config YAMLs changed `runtime.mcp_servers.*` -> `examples.incident_management.mcp_servers.*`. `git grep -E 'runtime\\.mcp_servers|runtime/mcp_servers' -- src/ tests/ apps/ scripts/ examples/ config/` returns zero hits. * Ratchet allowlist (tests/test_concept_leak_ratchet.py): removed the two `src/runtime/mcp_servers/remediation.py` entries (`apply_fix`, `notify_oncall`) and the explanatory Phase-7 comment. The `test_allowlist_entries_actually_match` meta-test stays GREEN because the file no longer exists under that path — leaving the entries would have failed the meta-test (stale allowlist). * Test contract migration: tests/test_notify_oncall_team_required.py was importing the now-removed `set_escalation_teams` directly. It now binds the roster via the public `register(mcp_app, cfg)` contract with a SimpleNamespace cfg — same observable behavior, same three test cases. tests/test_resume.py cfg fixture: added `mcp_servers=[...]` to OrchestratorConfig so the orchestrator's new discovery loop binds the escalation roster (otherwise the snapshot would carry over from prior tests, a fixture-completeness bug exposed by the contract change — Rule 3). Verification: * git ls-files src/runtime/mcp_servers/ -> empty * git ls-files examples/incident_management/mcp_servers/ -> 4 files (__init__, observability, remediation, user_context) * full pytest suite: 899 passed (baseline preserved) * ruff check (touched files): All checks passed * dist/apps/incident-management.py: contains the 3 relocated MCP server bodies (verified via grep for FastMCP("observability") etc.) * dist/apps/code-review.py: contains zero examples.incident_management.mcp_servers references — D-07-02 empty-list branch GREEN. Closes DECOUPLE-04 from REQUIREMENTS.md. * feat(08-01): pluggable state schema + code_review e2e + binary ratchet (DECOUPLE-05, DECOUPLE-07) — v1.1 milestone close Closes milestone v1.1 (Framework De-coupling). Single atomic commit per D-08-04 covering DECOUPLE-05 + DECOUPLE-07 + ratchet binarization. DECOUPLE-05 (D-08-01, D-08-02): * OrchestratorConfig.state_overrides_schema: str | None — dotted path to a pydantic BaseModel subclass (`mod.path:Class` or `mod.path.Class`). * importlib resolution at Orchestrator.create(); bad path raises RuntimeError with the offending path AND class name. Non-BaseModel targets are caught explicitly. * start_session(state_overrides=…) runs cls.model_validate(...) after _coerce_state_overrides and before store.create. None skips validation entirely — D-08-02 backward compatibility. * New IncidentStateOverrides (environment, severity) and CodeReviewStateOverrides (pr_url, repo, base_branch, pr_number). Both extra='forbid' so typos surface at session-start, not at gateway-eval (PVC-06 generalization). * All three runtime YAMLs declare state_overrides_schema. DECOUPLE-07 (D-08-03): * TerminalToolRule.match_args: dict[str, str] = {} — NEW optional argument-value discriminator. Empty default preserves v1.0 single- rule shape; non-empty restricts the rule to tool calls whose args[k] == v for every declared key. Generic feature any app can use (priority, severity, recommendation, …). * code_review.runtime.yaml: 5-status vocab + 3 set_recommendation rules dispatching by args.recommendation -> approved / changes_requested / commented; default_terminal_status: unreviewed. * tests/test_code_review_e2e.py (6 tests): real Orchestrator from YAML + synthesized set_recommendation ToolCall + finalize hook. Asserts code_review-vocabulary status (NOT unreviewed/needs_review), state_overrides validation rejects unknown keys + cross-app shapes, no incident_management modules in sys.modules after the test. Concept-leak ratchet binary closure: * Scrubbed mark_resolved/mark_escalated/notify_oncall/apply_fix from src/runtime/terminal_tools.py:19-20,45-47 and src/runtime/storage/event_log.py:3,37. Replacement uses neutral <terminal_tool> / set_recommendation placeholders. * RATCHET_ALLOWLIST = {} (was 5 entries). * git grep '\b(mark_(resolved|escalated)|notify_oncall|submit_hypothesis|update_incident|apply_fix)\b' src/runtime/ — zero matches. Bundler: * INCIDENT_APP_MODULE_ORDER and CODE_REVIEW_APP_MODULE_ORDER list their app's state.py first. dist/* regenerated; both schema classes embedded; ast.parse() OK. Tests: 927 passed (was 899) — 22 new schema + 6 new e2e. ruff clean on all touched files. RATCHET_ALLOWLIST: {}. Closes DECOUPLE-05 and DECOUPLE-07 — 7/7 v1.1 DECOUPLE-* shipped. Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com> * checkpoint: pre-yolo 2026-05-07T02:33:49 * fix(tests): set OLLAMA_API_KEY placeholder in test_default_yaml_loads_with_schema CI was missing OLLAMA_API_KEY env var, causing _interpolate to KeyError when loading config/config.yaml. Tests should not depend on real secrets — set a placeholder via monkeypatch so the YAML parses without making real Ollama calls. Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com> * fix(tests): cover all 5 env-var placeholders in test_default_yaml_loads_with_schema Prior fix only set OLLAMA_API_KEY but config.yaml has 5 `${VAR}` refs: OLLAMA_API_KEY, AZURE_ENDPOINT, AZURE_OPENAI_KEY, EXTERNAL_MCP_URL, EXT_TOKEN. Set placeholders for all of them so _interpolate succeeds in CI without any real secrets. Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com> --------- Co-authored-by: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
1 parent 02378dd commit 0ff8914

63 files changed

Lines changed: 9528 additions & 1630 deletions

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

config/code_review.runtime.yaml

Lines changed: 53 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -34,10 +34,63 @@ mcp:
3434
paths:
3535
skills_dir: examples/code_review/skills
3636
incidents_dir: code_reviews
37+
# Phase 8 (DECOUPLE-05 + DECOUPLE-07): full terminal-tool registry +
38+
# state-overrides schema for code_review. Three rules dispatch
39+
# ``set_recommendation`` by ``args.recommendation`` (approve ->
40+
# approved; request_changes -> changes_requested; comment -> commented).
41+
# When no rule fires the session falls through to ``unreviewed``
42+
# (the v1.0 framework-default failure mode).
3743
orchestrator:
3844
entry_agent: intake
45+
default_terminal_status: unreviewed
46+
statuses:
47+
in_review:
48+
name: in_review
49+
terminal: false
50+
kind: pending
51+
approved:
52+
name: approved
53+
terminal: true
54+
kind: success
55+
changes_requested:
56+
name: changes_requested
57+
terminal: true
58+
kind: failure
59+
commented:
60+
name: commented
61+
terminal: true
62+
kind: needs_review
63+
unreviewed:
64+
name: unreviewed
65+
terminal: true
66+
kind: needs_review
67+
terminal_tools:
68+
- tool_name: set_recommendation
69+
status: approved
70+
match_args:
71+
recommendation: approve
72+
- tool_name: set_recommendation
73+
status: changes_requested
74+
match_args:
75+
recommendation: request_changes
76+
- tool_name: set_recommendation
77+
status: commented
78+
match_args:
79+
recommendation: comment
80+
# App-MCP-server discovery (Phase 7 / DECOUPLE-04 / D-07-02). Empty
81+
# list = code-review boots without app-owned MCP servers; framework
82+
# treats the empty branch gracefully.
83+
mcp_servers: []
84+
# DECOUPLE-05 / D-08-01: app-declared pydantic schema for
85+
# state_overrides; orchestrator validates start_session's
86+
# state_overrides kwarg against this class.
87+
state_overrides_schema: examples.code_review.state.CodeReviewStateOverrides
3988
# Cross-cutting framework knobs read directly off AppConfig.framework.
4089
framework:
90+
# Per-app session-id prefix. Threaded through SessionStore into
91+
# Session.id_format so this app's rows look like REVIEW-YYYYMMDD-NNN
92+
# — disjoint from the incident-management ``INC-`` namespace.
93+
session_id_prefix: REVIEW
4194
confidence_threshold: 0.7
4295
similarity_threshold: 0.3
4396
escalation_teams: []

config/config.yaml

Lines changed: 65 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -47,15 +47,15 @@ mcp:
4747
category: incident_management
4848
- name: local_observability
4949
transport: in_process
50-
module: runtime.mcp_servers.observability
50+
module: examples.incident_management.mcp_servers.observability
5151
category: observability
5252
- name: local_remediation
5353
transport: in_process
54-
module: runtime.mcp_servers.remediation
54+
module: examples.incident_management.mcp_servers.remediation
5555
category: remediation
5656
- name: local_user_context
5757
transport: in_process
58-
module: runtime.mcp_servers.user_context
58+
module: examples.incident_management.mcp_servers.user_context
5959
category: user_context
6060
- name: external_ticketing
6161
transport: http
@@ -73,6 +73,9 @@ paths:
7373
# ``config/incident_management.yaml`` carries — kept here so the
7474
# bundled deployment config is self-contained.
7575
framework:
76+
# Per-app session-id prefix. Threaded through SessionStore into
77+
# Session.id_format so this app's rows look like INC-YYYYMMDD-NNN.
78+
session_id_prefix: INC
7679
confidence_threshold: 0.75
7780
similarity_threshold: 0.2
7881
escalation_teams:
@@ -124,8 +127,65 @@ dedup:
124127
same_environment: true
125128
only_closed: true
126129

127-
# orchestrator: omitted — framework defaults apply (entry_agent='intake').
128-
# Override here only if you want to point the entry at a different skill.
130+
# Generic terminal-tool registry. Apps declare which tool calls
131+
# transition the session to which status, plus optional per-rule
132+
# extra-field extraction. Replaces the v1.0 hardcoded
133+
# ``_TERMINAL_TOOL_RULES`` table in ``orchestrator.py`` (Phase 6 /
134+
# DECOUPLE-02 / DECOUPLE-03 / D-06-01..06). Mirrors
135+
# ``incident_management.yaml`` since this is the bundled deployment
136+
# config for the example app.
137+
orchestrator:
138+
entry_agent: intake
139+
default_terminal_status: needs_review
140+
statuses:
141+
open:
142+
name: open
143+
terminal: false
144+
kind: pending
145+
escalated:
146+
name: escalated
147+
terminal: true
148+
kind: escalation
149+
resolved:
150+
name: resolved
151+
terminal: true
152+
kind: success
153+
needs_review:
154+
name: needs_review
155+
terminal: true
156+
kind: needs_review
157+
terminal_tools:
158+
- tool_name: mark_resolved
159+
status: resolved
160+
- tool_name: mark_escalated
161+
status: escalated
162+
extract_fields:
163+
team:
164+
- args.team
165+
- result.team
166+
- tool_name: notify_oncall
167+
status: escalated
168+
extract_fields:
169+
team:
170+
- args.team
171+
patch_tools:
172+
- update_incident
173+
harvest_terminal_tools:
174+
- submit_hypothesis
175+
# App-MCP-server discovery (Phase 7 / DECOUPLE-04 / D-07-02). Each
176+
# entry is a dotted module path imported at orchestrator boot; each
177+
# module exposes ``register(mcp_app, cfg)``. Replaces the v1.0
178+
# hardcoded framework-internal MCP-server imports + setter calls.
179+
mcp_servers:
180+
- examples.incident_management.mcp_servers.observability
181+
- examples.incident_management.mcp_servers.remediation
182+
- examples.incident_management.mcp_servers.user_context
183+
escalate_action_tool_name: notify_oncall
184+
escalate_action_default_team: platform-oncall
185+
# DECOUPLE-05 / D-08-01: app-declared pydantic schema for
186+
# state_overrides; orchestrator validates the start_session
187+
# kwarg against this class.
188+
state_overrides_schema: examples.incident_management.state.IncidentStateOverrides
129189
runtime:
130190
# Wires the orchestrator and storage layer to the incident-management
131191
# domain state class (see examples/incident_management/state.py).

config/config.yaml.example

Lines changed: 50 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -53,15 +53,15 @@ mcp:
5353
category: incident_management
5454
- name: local_observability
5555
transport: in_process
56-
module: runtime.mcp_servers.observability
56+
module: examples.incident_management.mcp_servers.observability
5757
category: observability
5858
- name: local_remediation
5959
transport: in_process
60-
module: runtime.mcp_servers.remediation
60+
module: examples.incident_management.mcp_servers.remediation
6161
category: remediation
6262
- name: local_user_context
6363
transport: in_process
64-
module: runtime.mcp_servers.user_context
64+
module: examples.incident_management.mcp_servers.user_context
6565
category: user_context
6666
# Example remote MCP — disabled by default
6767
- name: external_ticketing
@@ -87,6 +87,53 @@ paths:
8787
skills_dir: config/skills
8888
incidents_dir: incidents
8989

90+
# Generic terminal-tool registry (Phase 6 / D-06-01..06).
91+
orchestrator:
92+
entry_agent: intake
93+
default_terminal_status: needs_review
94+
statuses:
95+
open:
96+
name: open
97+
terminal: false
98+
kind: pending
99+
escalated:
100+
name: escalated
101+
terminal: true
102+
kind: escalation
103+
resolved:
104+
name: resolved
105+
terminal: true
106+
kind: success
107+
needs_review:
108+
name: needs_review
109+
terminal: true
110+
kind: needs_review
111+
terminal_tools:
112+
- tool_name: mark_resolved
113+
status: resolved
114+
- tool_name: mark_escalated
115+
status: escalated
116+
extract_fields:
117+
team:
118+
- args.team
119+
- result.team
120+
- tool_name: notify_oncall
121+
status: escalated
122+
extract_fields:
123+
team:
124+
- args.team
125+
patch_tools:
126+
- update_incident
127+
harvest_terminal_tools:
128+
- submit_hypothesis
129+
# App-MCP-server discovery (Phase 7 / DECOUPLE-04 / D-07-02).
130+
mcp_servers:
131+
- examples.incident_management.mcp_servers.observability
132+
- examples.incident_management.mcp_servers.remediation
133+
- examples.incident_management.mcp_servers.user_context
134+
escalate_action_tool_name: notify_oncall
135+
escalate_action_default_team: platform-oncall
136+
90137
intervention:
91138
# If the deep_investigator's reported confidence is below this threshold, the
92139
# graph pauses and surfaces an intervention prompt to the UI.

config/incident_management.yaml

Lines changed: 68 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -10,8 +10,76 @@
1010
store_path: incidents
1111
similarity_method: keyword
1212

13+
# Generic terminal-tool registry. Apps declare which tool calls
14+
# transition the session to which status, plus optional per-rule
15+
# extra-field extraction. Replaces the v1.0 hardcoded
16+
# ``_TERMINAL_TOOL_RULES`` table in ``orchestrator.py`` (Phase 6 /
17+
# DECOUPLE-02 / DECOUPLE-03 / D-06-01..06).
18+
orchestrator:
19+
entry_agent: intake
20+
default_terminal_status: needs_review
21+
statuses:
22+
open:
23+
name: open
24+
terminal: false
25+
kind: pending
26+
escalated:
27+
name: escalated
28+
terminal: true
29+
kind: escalation
30+
resolved:
31+
name: resolved
32+
terminal: true
33+
kind: success
34+
needs_review:
35+
name: needs_review
36+
terminal: true
37+
kind: needs_review
38+
terminal_tools:
39+
- tool_name: mark_resolved
40+
status: resolved
41+
- tool_name: mark_escalated
42+
status: escalated
43+
extract_fields:
44+
team:
45+
- args.team
46+
- result.team
47+
- tool_name: notify_oncall
48+
status: escalated
49+
extract_fields:
50+
team:
51+
- args.team
52+
# Tools whose ``args.patch`` blob the harvester folds into agent
53+
# confidence/signal/rationale (DECOUPLE-02 generalization).
54+
patch_tools:
55+
- update_incident
56+
# Tools the harvester treats as typed-terminal for confidence
57+
# capture but the orchestrator's finalize path does NOT transition
58+
# status on (the deep_investigator's hypothesis tool is a stage-
59+
# complete signal, not a session-end signal).
60+
harvest_terminal_tools:
61+
- submit_hypothesis
62+
# App-MCP-server discovery (Phase 7 / DECOUPLE-04 / D-07-02). Each
63+
# entry is a dotted module path imported at orchestrator boot; each
64+
# module exposes ``register(mcp_app, cfg)``. Replaces the v1.0
65+
# hardcoded framework-internal MCP-server imports + setter calls.
66+
mcp_servers:
67+
- examples.incident_management.mcp_servers.observability
68+
- examples.incident_management.mcp_servers.remediation
69+
- examples.incident_management.mcp_servers.user_context
70+
# Parameterized escalate path (Resolution A — Option B).
71+
escalate_action_tool_name: notify_oncall
72+
escalate_action_default_team: platform-oncall
73+
# DECOUPLE-05 / D-08-01: app-declared pydantic schema for
74+
# state_overrides; orchestrator validates the start_session
75+
# kwarg against this class.
76+
state_overrides_schema: examples.incident_management.state.IncidentStateOverrides
77+
1378
# Cross-cutting framework knobs the runtime consumes directly.
1479
framework:
80+
# Per-app session-id prefix. Threaded through SessionStore into
81+
# Session.id_format so this app's rows look like INC-YYYYMMDD-NNN.
82+
session_id_prefix: INC
1583
confidence_threshold: 0.75
1684
similarity_threshold: 0.2
1785
escalation_teams:

0 commit comments

Comments
 (0)