{"owner":"Tracer-Cloud","repo":"opensre","hasSkills":true,"hasMcp":false,"mcpConfig":null,"found":["CLAUDE.md","AGENTS.md"],"skills":{"CLAUDE.md":"@AGENTS.md @CLAUDE_PERSONAL.md\n","AGENTS.md":"## OpenSRE Development Reference\n\n## Build and Run commands\n\n- Build `make install` (sets up the project environment via `uv sync` and installs this repo in editable mode)\n- Run **`uv run opensre …`** from the repo root while developing — preferred approach, uses this checkout even if another `opensre` is on your `PATH`.\n- Use **`uv run python …`** for any Python commands.\n\n## Code Style\n\n- Use strict typing, follow DRY principle\n- One clear purpose per file (separation of concerns)\n- Keep docstrings concise and contract-focused. Use one sentence for straightforward\n  APIs; add only non-obvious invariants, failure behavior, or layering constraints\n  callers must understand. Keep bug history and implementation narration in tests,\n  commits, or the PR description instead. Do not remove meaningful rationale merely\n  to shorten a docstring.\n- Use named constants for HTTP status codes (`http.HTTPStatus`, e.g.\n  `HTTPStatus.PAYMENT_REQUIRED`) in both source and tests — never hardcoded\n  numeric literals like `402`.\n- Env-var names and shared static constants live under `config/`, never inline\n  in a feature module or duplicated across files. Put them in a domain module\n  under `config/constants/` (e.g. `config/constants/billing.py`,\n  `config/constants/llm.py`) — a leaf that any layer can import without a cycle —\n  and re-export via `config/constants/__init__.py`. Do **not** define shared env\n  names in `config/config.py`: it imports `config.llm_auth.*`, so a name it and\n  one of those modules both need would force a cyclic import. Only a name used\n  solely inside `config/config.py` (nothing it imports needs it) may live there.\n- Do not keep compatibility-only forwarding modules after refactors. Once imports and tests\n  are migrated, remove the old module path in the same change and use one canonical import path.\n- Test fakes: never inline a lambda that builds an ad-hoc `type(...)` object (or\n  nests another lambda) into `monkeypatch.setattr` / `patch`. Extract a named\n  `def` and pass it — `type(...)` inside the helper body is fine:\n\n  ```python\n  def _build_harness() -> Any:\n      return type(\"H\", (), {\"resolve_env_variables\": lambda _self: None})()\n\n\n  monkeypatch.setattr(startup, \"_build_harness\", _build_harness)\n  ```\n\n  Trivial lambdas (`lambda **_kw: None`, `lambda: sentinel`) stay inline.\n  Precedent: `gateway/tests/runtime/test_startup.py` (`_StubHarness`),\n  `tests/cli/test_integrations_setup_github.py` (`_prompt_answering`).\n- Protocol methods you **add or change** use a **docstring-only body** — no\n  `...`, no `pass`, no `raise NotImplementedError`, and never a docstring *plus*\n  a trailing `...`/`pass`. Precedent (all fully compliant):\n  `platform/filestorage/ports.py`, `core/agent/loop_host.py`,\n  `gateway/core/runtime/sink_protocol.py`, `core/llm/types.py`.\n\n  ```python\n  class ObjectStore(Protocol):\n      def put_object(self, key: str, data: bytes) -> None:\n          \"\"\"Store ``data`` under ``key``.\"\"\"\n  ```\n\n  **The codebase is not yet compliant, and no tool will tell you.** An AST scan\n  of product code counts 89 docstring-only Protocol methods against **108\n  `raise NotImplementedError` stubs in 23 files** (`core/agent_harness/ports.py`,\n  `platform/harness_ports.py`, `gateway/core/storage/session/binding_store.py` and\n  others). Those are pre-existing and out of scope for a drive-by — do not\n  mass-convert them, and do not cite a file as precedent without checking it.\n\n  Enforcement is review-only:\n  - CodeQL `py/ineffectual-statement` catches **only** the bare `...` form.\n  - `pass` and `raise NotImplementedError` trip nothing.\n  - mypy exempts Protocol bodies from \"missing return\", so `pass` under a\n    `-> list[str]` signature passes `make typecheck`.\n\n### Tests (high-signal, not exhaustive)\n\nPrefer a **small suite that pins real failure modes** over broad line coverage.\nOne test per distinct bug class; do not multiply cases that exercise the same\nbranch with different literals.\n\n**Write / keep tests for:**\n\n- Security and authorization (allowlists, request-scoped authority, cross-actor\n  or cross-channel isolation).\n- Correctness under concurrency, crash, or shutdown (cursors, in-flight work,\n  ack vs replay, drain vs approval wait).\n- Package / transport borders that prevent silent coupling (no peer imports,\n  session key shape unique to the surface).\n- Regressions that already bit review or production (the P1 that forced a fix).\n\n**Skip or thin (unless they are the *only* coverage of a contract):**\n\n- Happy-path “mock was called once” wrappers (client send, init posts status).\n- Pure string / vocabulary tables when approve/deny/leave-open paths already\n  cover the helper.\n- Redundant success variants of an ack or dispatch path already covered by\n  fail / cancel / on_handled cases.\n- Defensive parse / “empty on fetch failure” edges that do not move durable\n  state.\n- Stand-in tests that restate control-flow intent without exercising the real\n  loop or wiring (e.g. “`create_task` does not block the creator”).\n\n\n### Docs under `docs/`\n\n`docs/` is user-facing. Test every sentence: *does this change what the reader\ndoes?* If not, cut it.\n\n- Cut vendor API endpoints (`getMe`), internal function names, which credential\n  tier a value lands in, and the bug a change fixed — that belongs in the PR\n  description or a module docstring.\n- Keep required vs optional, shortcuts that save real work, gotchas that are\n  invisible until they bite, and the exact commands and env vars they type.\n- Say \"a chat the bot was never added to fails during setup\", not \"`getChat`\n  returns `ok: false`\".\n\n### Performance (algorithms & data structures)\n\nApply on the **hot path** (per-request / per-iteration / per-tool-call); leave cold\ncode simple. Complexity must be deliberate — the simplest structure that meets the\nasymptotic need, and no more.\n\n- **Membership / dedup in a loop → `set`/`dict`, never `x in list`.** List `in` is\n  O(n); a set is O(1). (frozen dataclasses are hashable, so `set()` works on them.)\n- **Resolve once, reuse.** Don't re-scan for something already looked up — if you\n  fetched an object from a map, read its fields directly instead of a second linear\n  scan. Build an O(1) `{name: obj}` map instead of scanning a list by name.\n- **No `deepcopy` / `json.dumps` on the hot path.** If the result is invariant after\n  construction, compute it once (`functools.cached_property` / a stored field) and\n  treat it read-only. Verify no caller mutates a shared cached object first.\n- **Sort the light thing, once.** Sort keys/names (strings), not heavy objects, and\n  don't re-sort the same collection twice for two outputs.\n- **Right structure for the job:** `collections.deque` for queues/both-ends,\n  `heapq` for top-k, `bisect` for sorted search, `OrderedDict.move_to_end` /\n  `functools.lru_cache` for bounded caches, `\"\".join(parts)` never `+=` in a loop.\n- **Behavior-preserving refactors are TDD-guarded:** add a characterization test that\n  pins the observable behavior, confirm it passes on the *pre*-refactor code, then\n  keep it green through the change. Optimize only after; keep any benchmark in the PR.\n\n### File placement (all packages)\n\nWhen adding or changing behavior, put code in the **owning module first** — not the nearest\nshared file that already imports something similar.\n\n| Kind of file | Should contain | Should not contain |\n| --- | --- | --- |\n| **Orchestration** (`flow.py`, `controller.py`, `lifecycle.py`, `factory.py`) | Stage ordering, wiring, dispatch to specialists | Vendor/provider/domain logic, API clients, heavy UI |\n| **Shared UI / prompts** (`_ui.py`, `prompts.py`, generic `validation.py`) | Reusable prompts, tables, rendering, thin dispatch | Logic for one provider, integration, or vendor |\n| **Domain / provider / vendor module** (`providers/<name>.py`, `surfaces/cli/wizard/<name>.py`, `integrations/<vendor>/`) | All behavior specific to that provider, vendor, or feature area | Unrelated providers or cross-cutting orchestration |\n| **Registry / catalog** (`config.py`, `*_catalog.py`, `provider_registry.py`) | Metadata, defaults, discovery tables | Live API calls, onboarding prompts, retry loops |\n\n**Rules:**\n\n1. **Two or more functions for the same provider, vendor, or feature area** → add or extend\n   a dedicated module (or subpackage) for that area. Do not grow a shared orchestration or UI\n   file with provider-specific branches.\n2. **Before editing a shared file**, check for an existing sibling pattern in the same\n   package (`local_llm/`, `providers/azure_openai.py`, `integrations/<vendor>/tools/`, etc.).\n   Match that layout before inventing a new one.\n3. **Keep dispatch thin.** Shared entrypoints (`validate_provider_credentials`, `get_llm`,\n   slash-command handlers) should delegate in a few lines; implementation lives downstream.\n4. **Respect package boundaries** in [ARCHITECTURE.md](docs/ARCHITECTURE.md). Surfaces compose\n   lower tiers; `core/` and `integrations/` do not import from `surfaces/`.\n5. **Package-local detail** lives in that package's `AGENTS.md` when present (e.g.\n   [`surfaces/interactive_shell/AGENTS.md`](surfaces/interactive_shell/AGENTS.md),\n   [`core/llm/AGENTS.md`](core/llm/AGENTS.md)). Read it before structural changes in that tree.\n6. **Tool location** follows [docs/tool-placement-policy.md](docs/tool-placement-policy.md)\n   (vendor-specific vs `tools/system/` vs cross-vendor).\n\nIf a change would add a new provider-specific `if provider.value == ...` block to a file\nthat already serves multiple providers, stop and extract a dedicated module instead.\n\nBefore any push or PR creation follow [**CI.md**](CI.md) — lint, format, typecheck, and test commands all live there.\n\nWhen opening a PR, fill out the [**PR template**](.github/PULL_REQUEST_TEMPLATE.md) — it is not optional boilerplate; it has a required AI-usage disclosure section.\n\n## 1. Repo Map\n\n| Path                                          | What it does                                                                                                                                                                                                                                                                                                                           |\n| --------------------------------------------- | -------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |\n| `bootstrap/`                                  | Composition root: shared process boot (`process.py` — env, Sentry, adapters, capability warnings, LLM preload as an ordered `BootStep` table) and the registration steps themselves (`adapters.py`). Every host picks a `ProcessProfile` instead of writing its own boot order. The one package allowed to import `tools` and `integrations` together. |\n| `core/`                                       | Investigation orchestration, context assembly, the shared runtime tool-calling loop, and domain logic (state, types, correlation rules). Includes `core/tool_framework/` — the `BaseTool` base class, `@tool` decorator, registered-tool primitives, error telemetry, skill-guidance helpers, and shared payload utilities (`utils/`). |\n| `surfaces/cli/`                               | Command-line interface, onboarding wizard, local LLM helpers, and CLI tests support. Provider onboarding → `wizard/<provider>.py` (or `wizard/local_llm/`); new subcommands → `commands/<name>.py`. Runtime LLM wiring → [`core/llm/AGENTS.md`](core/llm/AGENTS.md).                                                                                                                                                                                                                                                   |\n| `surfaces/interactive_shell/`                 | Interactive terminal (REPL) loop, slash commands, chat/help surfaces, action-planning harness, and terminal UI.                                                                                                                                                                                                                        |\n| `integrations/`                               | Per-integration config normalization, verification, clients, helpers, store/catalog logic, the Hermes log pipeline, and per-vendor tool packages under `integrations/<vendor>/tools/`.                                                                                                                                                 |\n| `tools/`                                      | Tool registry, per-tool packages for cross-cutting tools that aren't vendor-specific (e.g. `tools/system/fleet_monitoring/`, `tools/system/watch_dog/`, `tools/system/sre_guidance_tool/`), and the interactive-shell action tools. Framework primitives (decorator, base class, utils) live in `core/tool_framework/`.                |\n| `config/`                                     | Shared constants, prompts, and UI theme.                                                                                                                                                                                                                                                                                               |\n| `tests/`                                      | Unit, integration, synthetic, deployment, e2e, chaos engineering, and support tests.                                                                                                                                                                                                                                                   |\n| `docs/`                                       | User-facing documentation, integration guides, and docs-site assets.                                                                                                                                                                                                                                                                   |\n| `.github/`                                    | CI workflows, issue templates, pull request template, and repository automation.                                                                                                                                                                                                                                                       |\n| `Dockerfile`                                  | Optional production container image (FastAPI health app via uvicorn).                                                                                                                                                                                                                                                                  |\n| `pyproject.toml`                              | Python project metadata, dependency configuration, tooling, and package settings.                                                                                                                                                                                                                                                      |\n| `Makefile`                                    | Canonical local automation for install, test, verify, deploy, and cleanup targets.                                                                                                                                                                                                                                                     |\n| `README.md`                                   | Product overview, install, quick start, high-level capabilities, and links to deeper docs.                                                                                                                                                                                                                                             |\n| `docs/DEVELOPMENT.md`                         | Contributor workflows: CI parity commands, dev container, benchmark, deployment, telemetry detail.                                                                                                                                                                                                                                     |\n| `docs/ARCHITECTURE.md`                        | Package architecture: the four-tier layer table, folder diagram, per-layer responsibilities, allowed cross-layer edges, and cross-layer flows.                                                                                                                                                                                         |\n| `docs/investigation-pipeline-architecture.md` | Investigation pipeline stages, ReAct loop control flow, and guardrails (tool cap, stagnation breaker, context budget), with diagrams.                                                                                                                                                                                                  |\n| `docs/investigation-tool-calling.md`          | Investigation ReAct tool schemas, LLM invoke payloads, and message shapes (all providers).                                                                                                                                                                                                                                             |\n| `docs/tool-placement-policy.md`               | Decision rule for where a tool lives: `integrations/<vendor>/tools/` vs. `tools/system/` vs. `tools/cross_vendor/` vs. `surfaces/shared/`.                                                                                                                                                                                             |\n| `docs/NAMING.md`                              | Naming conventions for `core/`: the glossary (State/Snapshot/RunInput/RunResult/Slice/Resources/Budget), the `{domain}_{role}.py` file rule, type naming (`Mixin` suffix, role-named Protocols, no package-name prefix), and anti-patterns.                                                                                            |\n| `SETUP.md`                                    | Machine setup (all platforms, Windows, MCP/OpenClaw, troubleshooting).                                                                                                                                                                                                                                                                 |\n| `CI.md`                                       | Mandatory pre-push checklist: lint, format, typecheck, tests — agents MUST follow before pushing.                                                                                                                                                                                                                                      |\n| `CONTRIBUTING.md`                             | Contribution workflow, branch/PR guidance, and quality expectations.                                                                                                                                                                                                                                                                   |\n\nMain packages one level deeper:\n\n- `platform/analytics/` — Analytics event plumbing and install helpers used by the onboarding flow.\n- `platform/auth/` — JWT and authentication helpers for local and hosted runtime access.\n- `surfaces/interactive_shell/` — REPL watchdog slash commands (`/watch`, `/watches`, `/unwatch`): PR demo steps live under **Interactive shell: REPL watchdog demo** in [docs/DEVELOPMENT.md](docs/DEVELOPMENT.md#interactive-shell-repl-watchdog-demo).\n- `config/constants/` — Shared prompt and other static constants.\n- `platform/deployment_ec2/` — EC2 AWS SDK primitives (`client`, `config`, EC2/IAM, SSM) and Telegram gateway AMI/systemd lifecycle (`telegram_gateway/`). Makefile: `make build-gateway-image`, `make deploy-gateway`.\n- `platform/guardrails/` — Guardrail rules, evaluation engine, audit helpers, and CLI bindings.\n- `platform/harness_ports.py` — Harness port layer (integration resolution, tool registry, investigation tools, GitHub repo scope). Real implementations are wired at startup via `integrations/harness_adapters.py` and `tools/harness_adapters.py` through `install_harness_ports()` in `surfaces/interactive_shell/ui/output/boundary.py`. See `core/agent_harness/AGENTS.md` for the import boundary.\n- `integrations/hermes/` — Hermes log tailing, incident classification, correlator, sinks, and investigation bridge.\n- `integrations/llm_cli/` — Subprocess-backed LLM CLIs (e.g. Codex). Extension guide: `integrations/llm_cli/AGENTS.md`.\n- `platform/masking/` — Masking utilities for redacting or normalizing sensitive content.\n- `tools/investigation/` — Composite investigation capability, public entrypoints, semantic stages, and reporting.\n- `core/llm/` — Hosted LLM provider clients, retry/schema helpers, and investigation tool-calling adapters.\n- `platform/sandbox/` — Sandboxed execution helpers for controlled runtime actions.\n- `core/state/` — Shared agent runtime envelope (`AgentState`), chat slice, investigation pipeline slice contracts, `EvidenceEntry`, state-update helpers, and pure defaults.\n- `core/domain/types/` — Shared typed contracts for evidence, retrieval, and tool-related payloads.\n- `tools/system/watch_dog/` — Watchdog feature: per-threshold alarm dispatch with cooldown (`--provider telegram|rocketchat`), sitting on top of `integrations/telegram/*` and `integrations/rocketchat/*`.\n- `gateway/web/webapp.py` — Web-facing health app served by the gateway daemon; the `opensre` CLI is `surfaces/cli/app.py`.\n\n## 2. Entry Points\n\n### Adding a Tool\n\nThe tool registry auto-discovers modules under `tools/`, so the normal path is to add one module or package there and let discovery pick it up. See [docs/adding-tools-and-integrations.md](docs/adding-tools-and-integrations.md) for the full file list and the detailed definition of done (package structure, contract/implementation rules, live-payload parsing, required docs/tests).\n\nSteps:\n\n1. Pick the simplest shape that fits the tool. Use a `BaseTool` subclass (from `core.tool_framework.base`) for richer behavior; use `@tool(...)` from `core.tool_framework.tool_decorator` for a lightweight function tool.\n2. Declare clear metadata: `name`, `description`, `source`, `input_schema`, and any `use_cases`, `requires`, `outputs`, or `retrieval_controls` you need.\n3. Before opening or approving the PR, follow [docs/adding-tools-and-integrations.md](docs/adding-tools-and-integrations.md).\n\n### Changing the investigation pipeline\n\nInvestigations are coordinated in `tools/investigation/lifecycle.py` and exposed via\n`tools/investigation/capability.py`. Semantic stages live under\n`tools/investigation/stages/`; reporting lives under\n`tools/investigation/reporting/`. See\n[docs/investigation-pipeline-architecture.md](docs/investigation-pipeline-architecture.md)\nfor the end-to-end stage/loop diagrams before making structural changes.\n\nFiles to touch:\n\n- `tools/investigation/lifecycle.py` for high-level stage ordering.\n- `core/state/` for shared agent state and investigation pipeline slice contracts\n  that cross stage boundaries.\n- `core/domain/` for pure investigation rules (alert source mapping, tool planning,\n  category alignment, correlation scoring).\n- `core/` for shared LLM runtime helpers (tool loop and LLM invoke error\n  classification).\n- `core/state/*.py` when adding or renaming persisted investigation fields\n  (update `AgentStateModel` and the matching slice).\n- `docs/` — update or add a page if the change introduces user-visible behavior or configuration.\n- `tests/` coverage for the affected CLI, synthetic, or integration paths.\n\nSteps:\n\n1. Keep each stage focused on one responsibility.\n2. Extend state models when new fields cross stage boundaries.\n3. Update tests that exercise `run_investigation` / streaming entry points.\n\n### Adding an Integration\n\nIntegration work usually spans config normalization, verification, integration-local clients/helpers, tools, docs, and tests. See [docs/adding-tools-and-integrations.md](docs/adding-tools-and-integrations.md) for the full file list, examples from the repo (Datadog, Grafana, Hermes), and the detailed definition of done (core completeness, investigation wiring, docs/tests, `make verify-integrations`, final demo gate).\n\nSteps:\n\n1. Add the integration config and normalization logic first so the rest of the stack can consume a consistent shape.\n2. Wire the tool layer after the config path is stable.\n3. Before opening or approving the PR, follow [docs/adding-tools-and-integrations.md](docs/adding-tools-and-integrations.md).\n\n## 3. Footguns (common mistakes to avoid)\n\n- Constant-condition toggles: never disable product logic with\n  `if False and …`, `if True or …`, or `if False:` to silence a failing test.\n  That hides real behavior (e.g. cancel short-circuit after action) and ships\n  as dead code. Keep the real condition and fix the test, or delete the branch.\n  Enforced by `tests/quality/test_no_constant_condition_toggles.py`.\n- No planning-stage fail-closed safeguard (v0.1): the interactive-shell action planner never denies a turn — do **not** reintroduce a planner denial, `mark_unhandled`, or the `UNHANDLED:` convention. Full rationale: [docs/interactive-shell-action-policy.md](docs/interactive-shell-action-policy.md); package rule: `surfaces/interactive_shell/AGENTS.md` (\"Action Selection And Execution\").\n- Docs navigation: Adding an `.mdx` file under `docs/` is not enough — Mintlify only shows pages listed in `docs/docs.json`. Forgetting the `pages` entry leaves the doc unreachable from the site sidebar.\n- Investigation tool schemas: draft-07 JSON Schema (e.g. `\"type\": [\"object\", \"null\"]`) can pass loose checks but fail the LLM API on first invoke because **all** available investigation tools are sent together. Normalize in the provider adapter and extend registry contract tests; see [docs/investigation-tool-calling.md](docs/investigation-tool-calling.md).\n- Action-agent path: do not implement regex/keyword/fuzzy intent routing or deterministic action bypasses around the action agent — including in the harness orchestrator / `SessionGoal` loop / evidence-tier policy. Intent belongs in the action turn (structured handoff tags such as `evidence_kind:…`, `session_goal:…`, `database_query:…`); hosts react to those tags or explicit APIs only. See `surfaces/interactive_shell/AGENTS.md` (\"Action Selection And Execution\") for the sanctioned literal-`/slash` exception, and `core/agent_harness/AGENTS.md`.\n- Information exposure through an exception (CWE-209 / CodeQL `py/stack-trace-exposure`): never send an exception's detail — `str(exc)`, `repr(exc)`, `traceback.format_exc()`, `exc.args`, provider/model/field internals — to an **external surface**. External surfaces are HTTP responses (`JSONResponse`/`HTTPException.detail` in `gateway/web/`) and chat gateway messages delivered to Slack/Telegram users (`OutputSink.render_error` on the gateway sinks). Log full detail server-side (`logger` + `capture_exception`) and return a generic message or `type(exc).__name__` only. The local CLI/terminal sink is **not** external — it may show detail. Redact at the sink/response boundary, not per call site, so the shared turn engine keeps detail for local dev.\n- Cyclic imports (CodeQL `py/cyclic-import`): CodeQL counts **function-local** and `TYPE_CHECKING` imports as part of a cycle, so making an import lazy does **not** clear the alert. Break the cycle structurally — move the shared symbol (type, exception, helper) into a **leaf** module both sides import, and never add a back-edge from a lower-level module up to a higher-level one. Precedent: `surfaces/cli/wizard/validation_result.py` and `surfaces/cli/llm_auth/persist.py` exist only to hold shared symbols so `validation` ↔ `azure_openai` and `_ui` → `service` stay acyclic.\n- CodeQL does not model `NoReturn`: it treats `pytest.skip`, `pytest.fail`, `sys.exit`, `typer.Exit` and custom raise-helpers as if they return, so any code after them looks reachable. Two alerts come from this — `py/uninitialized-local-variable` when a name is bound in `try` and the `except` only calls such a function, and unreachable-code when a `with` body ends in a bare `raise`. Do **not** silence with a comment: bind the name on every path CodeQL can see. Prefer a sentinel over exception control flow for ordinary \"not found\" — `next(iterable, None)` plus an explicit `if x is None:` guard, not `try: next(...) except StopIteration:`. `mypy` narrows correctly after the guard because it *does* honour `NoReturn`. For the bare-`raise` case, extract a `_raise()` helper.\n- Protocol stub bodies (CodeQL `py/ineffectual-statement`): a bare `...` on a\n  `Protocol` method is a valid PEP-544 idiom but trips CodeQL as a statement\n  with no effect. Do **not** write `def foo(self) -> T: ...`. Use a one-line\n  docstring as the only body (see Code Style above), and do not keep both a\n  docstring and a trailing `...`/`pass`. Prefer documenting the contract on the\n  port over silencing the alert with a comment. Note the scope: CodeQL catches\n  only `...`, so a clean scan does **not** mean the codebase follows the rule —\n  `pass` and `raise NotImplementedError` are invisible to it, and 108 of the\n  latter remain.\n- `py/ineffectual-statement` does **not** understand `await`: a bare\n  `await some_task` reads to CodeQL as a discarded expression. It is not — the\n  await is the side effect (e.g. reaping a cancelled task so `client.close()`\n  runs). Do **not** delete the await. Prefer a small helper that binds the\n  result (`_finished = await task` in `gateway/transports/discord/worker.py`\n  `_reap_cancelled_task`) over a bare expression statement; do not \"fix\" by\n  skipping the await.\n- Implicit string concatenation in a list (CodeQL\n  `py/implicit-string-concatenation-in-list`): two adjacent string literals\n  inside a list/tuple display are indistinguishable from a **missing comma**,\n  so a long message split over two lines trips it. Do not silence it with an\n  explicit `+` — extract the text to a module constant and reference that. The\n  same implicit concatenation is fine in a parenthesised assignment, where no\n  comma could have been intended. Precedent:\n  `tools/system/python_execution_tool/__init__.py`\n  (`_RUNTIME_FACTS_ANTI_EXAMPLE`). Bites hardest in `use_cases` /\n  `anti_examples` / `examples` tool metadata, where entries are prose and\n  routinely exceed the 100-char line limit.\n- Mixed import styles (CodeQL `py/import-and-import-from`): importing one\n  module with both `import X as alias` and `from X import name` — even when\n  the `from` import is function-local — raises an alert. It usually happens\n  when appending to an existing file: **use the import style the file already\n  established** (an existing `import core.context_budget as budget` means new\n  code calls `budget.name`, not `from core.context_budget import name`).\n- Except block handles `BaseException` (code-quality): catch `Exception`, not\n  `BaseException`. Collecting request/transport failures in a test thread still\n  works — `requests.exceptions.ConnectTimeout` subclasses `Exception`. Catching\n  `BaseException` also swallows `KeyboardInterrupt` / `SystemExit`. Do not keep\n  `noqa: BLE001` to silence it.\n- Unused global variable (CodeQL / code-quality \"Unused global variable\"):\n  CodeQL often **does not credit cross-module imports** as a use of a module-\n  level constant. A `FOO = \"...\"` in `text.py` that is only read via\n  `from …text import FOO` in another file can still alert. Prefer keeping\n  related copy in a structure that is clearly used in the defining module\n  (e.g. a dict entry under `HANDOFF_GUIDANCE[\"database_query:\"]` with prefix\n  matching in the consumer), or co-locate the constant with its only reader.\n  Do **not** add a no-op self-reference or `# noqa` just to silence the alert.\n- Shared client state under concurrent turns: LLM clients are cached per role\n  (`get_llm`) and the gateway runs turns in parallel, so **one client instance\n  serves several in-flight requests**. An instance flag mutated inside an error\n  handler is then read by requests that were already built under the old value.\n  Branch on **what the current request carried**, not on the flag's present\n  value — capture the fact locally where the request is built\n  (`marked = strip_cache_markers(kwargs) != kwargs`) and consult that in the\n  `except`. Precedent: the prompt-cache fallback, where a first 400 cleared\n  `_cache_markers_enabled` and a second already-marked request skipped its\n  uncached retry and failed the turn. Test it by having the fake dependency\n  mutate the shared state *before* raising, which reproduces the race\n  deterministically without threads.\n- CI typecheck does **not** cover `tests/`: `make typecheck` runs mypy over `PYTHON_SOURCE_PATHS` (`config core gateway integrations platform surfaces tools`) only. Type errors in test files never fail CI, so do not assume a clean `make typecheck` means the tests you just wrote are type-clean — run mypy on the test path directly when it matters.\n\n"},"files":{"CLAUDE.md":"@AGENTS.md @CLAUDE_PERSONAL.md\n","AGENTS.md":"## OpenSRE Development Reference\n\n## Build and Run commands\n\n- Build `make install` (sets up the project environment via `uv sync` and installs this repo in editable mode)\n- Run **`uv run opensre …`** from the repo root while developing — preferred approach, uses this checkout even if another `opensre` is on your `PATH`.\n- Use **`uv run python …`** for any Python commands.\n\n## Code Style\n\n- Use strict typing, follow DRY principle\n- One clear purpose per file (separation of concerns)\n- Keep docstrings concise and contract-focused. Use one sentence for straightforward\n  APIs; add only non-obvious invariants, failure behavior, or layering constraints\n  callers must understand. Keep bug history and implementation narration in tests,\n  commits, or the PR description instead. Do not remove meaningful rationale merely\n  to shorten a docstring.\n- Use named constants for HTTP status codes (`http.HTTPStatus`, e.g.\n  `HTTPStatus.PAYMENT_REQUIRED`) in both source and tests — never hardcoded\n  numeric literals like `402`.\n- Env-var names and shared static constants live under `config/`, never inline\n  in a feature module or duplicated across files. Put them in a domain module\n  under `config/constants/` (e.g. `config/constants/billing.py`,\n  `config/constants/llm.py`) — a leaf that any layer can import without a cycle —\n  and re-export via `config/constants/__init__.py`. Do **not** define shared env\n  names in `config/config.py`: it imports `config.llm_auth.*`, so a name it and\n  one of those modules both need would force a cyclic import. Only a name used\n  solely inside `config/config.py` (nothing it imports needs it) may live there.\n- Do not keep compatibility-only forwarding modules after refactors. Once imports and tests\n  are migrated, remove the old module path in the same change and use one canonical import path.\n- Test fakes: never inline a lambda that builds an ad-hoc `type(...)` object (or\n  nests another lambda) into `monkeypatch.setattr` / `patch`. Extract a named\n  `def` and pass it — `type(...)` inside the helper body is fine:\n\n  ```python\n  def _build_harness() -> Any:\n      return type(\"H\", (), {\"resolve_env_variables\": lambda _self: None})()\n\n\n  monkeypatch.setattr(startup, \"_build_harness\", _build_harness)\n  ```\n\n  Trivial lambdas (`lambda **_kw: None`, `lambda: sentinel`) stay inline.\n  Precedent: `gateway/tests/runtime/test_startup.py` (`_StubHarness`),\n  `tests/cli/test_integrations_setup_github.py` (`_prompt_answering`).\n- Protocol methods you **add or change** use a **docstring-only body** — no\n  `...`, no `pass`, no `raise NotImplementedError`, and never a docstring *plus*\n  a trailing `...`/`pass`. Precedent (all fully compliant):\n  `platform/filestorage/ports.py`, `core/agent/loop_host.py`,\n  `gateway/core/runtime/sink_protocol.py`, `core/llm/types.py`.\n\n  ```python\n  class ObjectStore(Protocol):\n      def put_object(self, key: str, data: bytes) -> None:\n          \"\"\"Store ``data`` under ``key``.\"\"\"\n  ```\n\n  **The codebase is not yet compliant, and no tool will tell you.** An AST scan\n  of product code counts 89 docstring-only Protocol methods against **108\n  `raise NotImplementedError` stubs in 23 files** (`core/agent_harness/ports.py`,\n  `platform/harness_ports.py`, `gateway/core/storage/session/binding_store.py` and\n  others). Those are pre-existing and out of scope for a drive-by — do not\n  mass-convert them, and do not cite a file as precedent without checking it.\n\n  Enforcement is review-only:\n  - CodeQL `py/ineffectual-statement` catches **only** the bare `...` form.\n  - `pass` and `raise NotImplementedError` trip nothing.\n  - mypy exempts Protocol bodies from \"missing return\", so `pass` under a\n    `-> list[str]` signature passes `make typecheck`.\n\n### Tests (high-signal, not exhaustive)\n\nPrefer a **small suite that pins real failure modes** over broad line coverage.\nOne test per distinct bug class; do not multiply cases that exercise the same\nbranch with different literals.\n\n**Write / keep tests for:**\n\n- Security and authorization (allowlists, request-scoped authority, cross-actor\n  or cross-channel isolation).\n- Correctness under concurrency, crash, or shutdown (cursors, in-flight work,\n  ack vs replay, drain vs approval wait).\n- Package / transport borders that prevent silent coupling (no peer imports,\n  session key shape unique to the surface).\n- Regressions that already bit review or production (the P1 that forced a fix).\n\n**Skip or thin (unless they are the *only* coverage of a contract):**\n\n- Happy-path “mock was called once” wrappers (client send, init posts status).\n- Pure string / vocabulary tables when approve/deny/leave-open paths already\n  cover the helper.\n- Redundant success variants of an ack or dispatch path already covered by\n  fail / cancel / on_handled cases.\n- Defensive parse / “empty on fetch failure” edges that do not move durable\n  state.\n- Stand-in tests that restate control-flow intent without exercising the real\n  loop or wiring (e.g. “`create_task` does not block the creator”).\n\n\n### Docs under `docs/`\n\n`docs/` is user-facing. Test every sentence: *does this change what the reader\ndoes?* If not, cut it.\n\n- Cut vendor API endpoints (`getMe`), internal function names, which credential\n  tier a value lands in, and the bug a change fixed — that belongs in the PR\n  description or a module docstring.\n- Keep required vs optional, shortcuts that save real work, gotchas that are\n  invisible until they bite, and the exact commands and env vars they type.\n- Say \"a chat the bot was never added to fails during setup\", not \"`getChat`\n  returns `ok: false`\".\n\n### Performance (algorithms & data structures)\n\nApply on the **hot path** (per-request / per-iteration / per-tool-call); leave cold\ncode simple. Complexity must be deliberate — the simplest structure that meets the\nasymptotic need, and no more.\n\n- **Membership / dedup in a loop → `set`/`dict`, never `x in list`.** List `in` is\n  O(n); a set is O(1). (frozen dataclasses are hashable, so `set()` works on them.)\n- **Resolve once, reuse.** Don't re-scan for something already looked up — if you\n  fetched an object from a map, read its fields directly instead of a second linear\n  scan. Build an O(1) `{name: obj}` map instead of scanning a list by name.\n- **No `deepcopy` / `json.dumps` on the hot path.** If the result is invariant after\n  construction, compute it once (`functools.cached_property` / a stored field) and\n  treat it read-only. Verify no caller mutates a shared cached object first.\n- **Sort the light thing, once.** Sort keys/names (strings), not heavy objects, and\n  don't re-sort the same collection twice for two outputs.\n- **Right structure for the job:** `collections.deque` for queues/both-ends,\n  `heapq` for top-k, `bisect` for sorted search, `OrderedDict.move_to_end` /\n  `functools.lru_cache` for bounded caches, `\"\".join(parts)` never `+=` in a loop.\n- **Behavior-preserving refactors are TDD-guarded:** add a characterization test that\n  pins the observable behavior, confirm it passes on the *pre*-refactor code, then\n  keep it green through the change. Optimize only after; keep any benchmark in the PR.\n\n### File placement (all packages)\n\nWhen adding or changing behavior, put code in the **owning module first** — not the nearest\nshared file that already imports something similar.\n\n| Kind of file | Should contain | Should not contain |\n| --- | --- | --- |\n| **Orchestration** (`flow.py`, `controller.py`, `lifecycle.py`, `factory.py`) | Stage ordering, wiring, dispatch to specialists | Vendor/provider/domain logic, API clients, heavy UI |\n| **Shared UI / prompts** (`_ui.py`, `prompts.py`, generic `validation.py`) | Reusable prompts, tables, rendering, thin dispatch | Logic for one provider, integration, or vendor |\n| **Domain / provider / vendor module** (`providers/<name>.py`, `surfaces/cli/wizard/<name>.py`, `integrations/<vendor>/`) | All behavior specific to that provider, vendor, or feature area | Unrelated providers or cross-cutting orchestration |\n| **Registry / catalog** (`config.py`, `*_catalog.py`, `provider_registry.py`) | Metadata, defaults, discovery tables | Live API calls, onboarding prompts, retry loops |\n\n**Rules:**\n\n1. **Two or more functions for the same provider, vendor, or feature area** → add or extend\n   a dedicated module (or subpackage) for that area. Do not grow a shared orchestration or UI\n   file with provider-specific branches.\n2. **Before editing a shared file**, check for an existing sibling pattern in the same\n   package (`local_llm/`, `providers/azure_openai.py`, `integrations/<vendor>/tools/`, etc.).\n   Match that layout before inventing a new one.\n3. **Keep dispatch thin.** Shared entrypoints (`validate_provider_credentials`, `get_llm`,\n   slash-command handlers) should delegate in a few lines; implementation lives downstream.\n4. **Respect package boundaries** in [ARCHITECTURE.md](docs/ARCHITECTURE.md). Surfaces compose\n   lower tiers; `core/` and `integrations/` do not import from `surfaces/`.\n5. **Package-local detail** lives in that package's `AGENTS.md` when present (e.g.\n   [`surfaces/interactive_shell/AGENTS.md`](surfaces/interactive_shell/AGENTS.md),\n   [`core/llm/AGENTS.md`](core/llm/AGENTS.md)). Read it before structural changes in that tree.\n6. **Tool location** follows [docs/tool-placement-policy.md](docs/tool-placement-policy.md)\n   (vendor-specific vs `tools/system/` vs cross-vendor).\n\nIf a change would add a new provider-specific `if provider.value == ...` block to a file\nthat already serves multiple providers, stop and extract a dedicated module instead.\n\nBefore any push or PR creation follow [**CI.md**](CI.md) — lint, format, typecheck, and test commands all live there.\n\nWhen opening a PR, fill out the [**PR template**](.github/PULL_REQUEST_TEMPLATE.md) — it is not optional boilerplate; it has a required AI-usage disclosure section.\n\n## 1. Repo Map\n\n| Path                                          | What it does                                                                                                                                                                                                                                                                                                                           |\n| --------------------------------------------- | -------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |\n| `bootstrap/`                                  | Composition root: shared process boot (`process.py` — env, Sentry, adapters, capability warnings, LLM preload as an ordered `BootStep` table) and the registration steps themselves (`adapters.py`). Every host picks a `ProcessProfile` instead of writing its own boot order. The one package allowed to import `tools` and `integrations` together. |\n| `core/`                                       | Investigation orchestration, context assembly, the shared runtime tool-calling loop, and domain logic (state, types, correlation rules). Includes `core/tool_framework/` — the `BaseTool` base class, `@tool` decorator, registered-tool primitives, error telemetry, skill-guidance helpers, and shared payload utilities (`utils/`). |\n| `surfaces/cli/`                               | Command-line interface, onboarding wizard, local LLM helpers, and CLI tests support. Provider onboarding → `wizard/<provider>.py` (or `wizard/local_llm/`); new subcommands → `commands/<name>.py`. Runtime LLM wiring → [`core/llm/AGENTS.md`](core/llm/AGENTS.md).                                                                                                                                                                                                                                                   |\n| `surfaces/interactive_shell/`                 | Interactive terminal (REPL) loop, slash commands, chat/help surfaces, action-planning harness, and terminal UI.                                                                                                                                                                                                                        |\n| `integrations/`                               | Per-integration config normalization, verification, clients, helpers, store/catalog logic, the Hermes log pipeline, and per-vendor tool packages under `integrations/<vendor>/tools/`.                                                                                                                                                 |\n| `tools/`                                      | Tool registry, per-tool packages for cross-cutting tools that aren't vendor-specific (e.g. `tools/system/fleet_monitoring/`, `tools/system/watch_dog/`, `tools/system/sre_guidance_tool/`), and the interactive-shell action tools. Framework primitives (decorator, base class, utils) live in `core/tool_framework/`.                |\n| `config/`                                     | Shared constants, prompts, and UI theme.                                                                                                                                                                                                                                                                                               |\n| `tests/`                                      | Unit, integration, synthetic, deployment, e2e, chaos engineering, and support tests.                                                                                                                                                                                                                                                   |\n| `docs/`                                       | User-facing documentation, integration guides, and docs-site assets.                                                                                                                                                                                                                                                                   |\n| `.github/`                                    | CI workflows, issue templates, pull request template, and repository automation.                                                                                                                                                                                                                                                       |\n| `Dockerfile`                                  | Optional production container image (FastAPI health app via uvicorn).                                                                                                                                                                                                                                                                  |\n| `pyproject.toml`                              | Python project metadata, dependency configuration, tooling, and package settings.                                                                                                                                                                                                                                                      |\n| `Makefile`                                    | Canonical local automation for install, test, verify, deploy, and cleanup targets.                                                                                                                                                                                                                                                     |\n| `README.md`                                   | Product overview, install, quick start, high-level capabilities, and links to deeper docs.                                                                                                                                                                                                                                             |\n| `docs/DEVELOPMENT.md`                         | Contributor workflows: CI parity commands, dev container, benchmark, deployment, telemetry detail.                                                                                                                                                                                                                                     |\n| `docs/ARCHITECTURE.md`                        | Package architecture: the four-tier layer table, folder diagram, per-layer responsibilities, allowed cross-layer edges, and cross-layer flows.                                                                                                                                                                                         |\n| `docs/investigation-pipeline-architecture.md` | Investigation pipeline stages, ReAct loop control flow, and guardrails (tool cap, stagnation breaker, context budget), with diagrams.                                                                                                                                                                                                  |\n| `docs/investigation-tool-calling.md`          | Investigation ReAct tool schemas, LLM invoke payloads, and message shapes (all providers).                                                                                                                                                                                                                                             |\n| `docs/tool-placement-policy.md`               | Decision rule for where a tool lives: `integrations/<vendor>/tools/` vs. `tools/system/` vs. `tools/cross_vendor/` vs. `surfaces/shared/`.                                                                                                                                                                                             |\n| `docs/NAMING.md`                              | Naming conventions for `core/`: the glossary (State/Snapshot/RunInput/RunResult/Slice/Resources/Budget), the `{domain}_{role}.py` file rule, type naming (`Mixin` suffix, role-named Protocols, no package-name prefix), and anti-patterns.                                                                                            |\n| `SETUP.md`                                    | Machine setup (all platforms, Windows, MCP/OpenClaw, troubleshooting).                                                                                                                                                                                                                                                                 |\n| `CI.md`                                       | Mandatory pre-push checklist: lint, format, typecheck, tests — agents MUST follow before pushing.                                                                                                                                                                                                                                      |\n| `CONTRIBUTING.md`                             | Contribution workflow, branch/PR guidance, and quality expectations.                                                                                                                                                                                                                                                                   |\n\nMain packages one level deeper:\n\n- `platform/analytics/` — Analytics event plumbing and install helpers used by the onboarding flow.\n- `platform/auth/` — JWT and authentication helpers for local and hosted runtime access.\n- `surfaces/interactive_shell/` — REPL watchdog slash commands (`/watch`, `/watches`, `/unwatch`): PR demo steps live under **Interactive shell: REPL watchdog demo** in [docs/DEVELOPMENT.md](docs/DEVELOPMENT.md#interactive-shell-repl-watchdog-demo).\n- `config/constants/` — Shared prompt and other static constants.\n- `platform/deployment_ec2/` — EC2 AWS SDK primitives (`client`, `config`, EC2/IAM, SSM) and Telegram gateway AMI/systemd lifecycle (`telegram_gateway/`). Makefile: `make build-gateway-image`, `make deploy-gateway`.\n- `platform/guardrails/` — Guardrail rules, evaluation engine, audit helpers, and CLI bindings.\n- `platform/harness_ports.py` — Harness port layer (integration resolution, tool registry, investigation tools, GitHub repo scope). Real implementations are wired at startup via `integrations/harness_adapters.py` and `tools/harness_adapters.py` through `install_harness_ports()` in `surfaces/interactive_shell/ui/output/boundary.py`. See `core/agent_harness/AGENTS.md` for the import boundary.\n- `integrations/hermes/` — Hermes log tailing, incident classification, correlator, sinks, and investigation bridge.\n- `integrations/llm_cli/` — Subprocess-backed LLM CLIs (e.g. Codex). Extension guide: `integrations/llm_cli/AGENTS.md`.\n- `platform/masking/` — Masking utilities for redacting or normalizing sensitive content.\n- `tools/investigation/` — Composite investigation capability, public entrypoints, semantic stages, and reporting.\n- `core/llm/` — Hosted LLM provider clients, retry/schema helpers, and investigation tool-calling adapters.\n- `platform/sandbox/` — Sandboxed execution helpers for controlled runtime actions.\n- `core/state/` — Shared agent runtime envelope (`AgentState`), chat slice, investigation pipeline slice contracts, `EvidenceEntry`, state-update helpers, and pure defaults.\n- `core/domain/types/` — Shared typed contracts for evidence, retrieval, and tool-related payloads.\n- `tools/system/watch_dog/` — Watchdog feature: per-threshold alarm dispatch with cooldown (`--provider telegram|rocketchat`), sitting on top of `integrations/telegram/*` and `integrations/rocketchat/*`.\n- `gateway/web/webapp.py` — Web-facing health app served by the gateway daemon; the `opensre` CLI is `surfaces/cli/app.py`.\n\n## 2. Entry Points\n\n### Adding a Tool\n\nThe tool registry auto-discovers modules under `tools/`, so the normal path is to add one module or package there and let discovery pick it up. See [docs/adding-tools-and-integrations.md](docs/adding-tools-and-integrations.md) for the full file list and the detailed definition of done (package structure, contract/implementation rules, live-payload parsing, required docs/tests).\n\nSteps:\n\n1. Pick the simplest shape that fits the tool. Use a `BaseTool` subclass (from `core.tool_framework.base`) for richer behavior; use `@tool(...)` from `core.tool_framework.tool_decorator` for a lightweight function tool.\n2. Declare clear metadata: `name`, `description`, `source`, `input_schema`, and any `use_cases`, `requires`, `outputs`, or `retrieval_controls` you need.\n3. Before opening or approving the PR, follow [docs/adding-tools-and-integrations.md](docs/adding-tools-and-integrations.md).\n\n### Changing the investigation pipeline\n\nInvestigations are coordinated in `tools/investigation/lifecycle.py` and exposed via\n`tools/investigation/capability.py`. Semantic stages live under\n`tools/investigation/stages/`; reporting lives under\n`tools/investigation/reporting/`. See\n[docs/investigation-pipeline-architecture.md](docs/investigation-pipeline-architecture.md)\nfor the end-to-end stage/loop diagrams before making structural changes.\n\nFiles to touch:\n\n- `tools/investigation/lifecycle.py` for high-level stage ordering.\n- `core/state/` for shared agent state and investigation pipeline slice contracts\n  that cross stage boundaries.\n- `core/domain/` for pure investigation rules (alert source mapping, tool planning,\n  category alignment, correlation scoring).\n- `core/` for shared LLM runtime helpers (tool loop and LLM invoke error\n  classification).\n- `core/state/*.py` when adding or renaming persisted investigation fields\n  (update `AgentStateModel` and the matching slice).\n- `docs/` — update or add a page if the change introduces user-visible behavior or configuration.\n- `tests/` coverage for the affected CLI, synthetic, or integration paths.\n\nSteps:\n\n1. Keep each stage focused on one responsibility.\n2. Extend state models when new fields cross stage boundaries.\n3. Update tests that exercise `run_investigation` / streaming entry points.\n\n### Adding an Integration\n\nIntegration work usually spans config normalization, verification, integration-local clients/helpers, tools, docs, and tests. See [docs/adding-tools-and-integrations.md](docs/adding-tools-and-integrations.md) for the full file list, examples from the repo (Datadog, Grafana, Hermes), and the detailed definition of done (core completeness, investigation wiring, docs/tests, `make verify-integrations`, final demo gate).\n\nSteps:\n\n1. Add the integration config and normalization logic first so the rest of the stack can consume a consistent shape.\n2. Wire the tool layer after the config path is stable.\n3. Before opening or approving the PR, follow [docs/adding-tools-and-integrations.md](docs/adding-tools-and-integrations.md).\n\n## 3. Footguns (common mistakes to avoid)\n\n- Constant-condition toggles: never disable product logic with\n  `if False and …`, `if True or …`, or `if False:` to silence a failing test.\n  That hides real behavior (e.g. cancel short-circuit after action) and ships\n  as dead code. Keep the real condition and fix the test, or delete the branch.\n  Enforced by `tests/quality/test_no_constant_condition_toggles.py`.\n- No planning-stage fail-closed safeguard (v0.1): the interactive-shell action planner never denies a turn — do **not** reintroduce a planner denial, `mark_unhandled`, or the `UNHANDLED:` convention. Full rationale: [docs/interactive-shell-action-policy.md](docs/interactive-shell-action-policy.md); package rule: `surfaces/interactive_shell/AGENTS.md` (\"Action Selection And Execution\").\n- Docs navigation: Adding an `.mdx` file under `docs/` is not enough — Mintlify only shows pages listed in `docs/docs.json`. Forgetting the `pages` entry leaves the doc unreachable from the site sidebar.\n- Investigation tool schemas: draft-07 JSON Schema (e.g. `\"type\": [\"object\", \"null\"]`) can pass loose checks but fail the LLM API on first invoke because **all** available investigation tools are sent together. Normalize in the provider adapter and extend registry contract tests; see [docs/investigation-tool-calling.md](docs/investigation-tool-calling.md).\n- Action-agent path: do not implement regex/keyword/fuzzy intent routing or deterministic action bypasses around the action agent — including in the harness orchestrator / `SessionGoal` loop / evidence-tier policy. Intent belongs in the action turn (structured handoff tags such as `evidence_kind:…`, `session_goal:…`, `database_query:…`); hosts react to those tags or explicit APIs only. See `surfaces/interactive_shell/AGENTS.md` (\"Action Selection And Execution\") for the sanctioned literal-`/slash` exception, and `core/agent_harness/AGENTS.md`.\n- Information exposure through an exception (CWE-209 / CodeQL `py/stack-trace-exposure`): never send an exception's detail — `str(exc)`, `repr(exc)`, `traceback.format_exc()`, `exc.args`, provider/model/field internals — to an **external surface**. External surfaces are HTTP responses (`JSONResponse`/`HTTPException.detail` in `gateway/web/`) and chat gateway messages delivered to Slack/Telegram users (`OutputSink.render_error` on the gateway sinks). Log full detail server-side (`logger` + `capture_exception`) and return a generic message or `type(exc).__name__` only. The local CLI/terminal sink is **not** external — it may show detail. Redact at the sink/response boundary, not per call site, so the shared turn engine keeps detail for local dev.\n- Cyclic imports (CodeQL `py/cyclic-import`): CodeQL counts **function-local** and `TYPE_CHECKING` imports as part of a cycle, so making an import lazy does **not** clear the alert. Break the cycle structurally — move the shared symbol (type, exception, helper) into a **leaf** module both sides import, and never add a back-edge from a lower-level module up to a higher-level one. Precedent: `surfaces/cli/wizard/validation_result.py` and `surfaces/cli/llm_auth/persist.py` exist only to hold shared symbols so `validation` ↔ `azure_openai` and `_ui` → `service` stay acyclic.\n- CodeQL does not model `NoReturn`: it treats `pytest.skip`, `pytest.fail`, `sys.exit`, `typer.Exit` and custom raise-helpers as if they return, so any code after them looks reachable. Two alerts come from this — `py/uninitialized-local-variable` when a name is bound in `try` and the `except` only calls such a function, and unreachable-code when a `with` body ends in a bare `raise`. Do **not** silence with a comment: bind the name on every path CodeQL can see. Prefer a sentinel over exception control flow for ordinary \"not found\" — `next(iterable, None)` plus an explicit `if x is None:` guard, not `try: next(...) except StopIteration:`. `mypy` narrows correctly after the guard because it *does* honour `NoReturn`. For the bare-`raise` case, extract a `_raise()` helper.\n- Protocol stub bodies (CodeQL `py/ineffectual-statement`): a bare `...` on a\n  `Protocol` method is a valid PEP-544 idiom but trips CodeQL as a statement\n  with no effect. Do **not** write `def foo(self) -> T: ...`. Use a one-line\n  docstring as the only body (see Code Style above), and do not keep both a\n  docstring and a trailing `...`/`pass`. Prefer documenting the contract on the\n  port over silencing the alert with a comment. Note the scope: CodeQL catches\n  only `...`, so a clean scan does **not** mean the codebase follows the rule —\n  `pass` and `raise NotImplementedError` are invisible to it, and 108 of the\n  latter remain.\n- `py/ineffectual-statement` does **not** understand `await`: a bare\n  `await some_task` reads to CodeQL as a discarded expression. It is not — the\n  await is the side effect (e.g. reaping a cancelled task so `client.close()`\n  runs). Do **not** delete the await. Prefer a small helper that binds the\n  result (`_finished = await task` in `gateway/transports/discord/worker.py`\n  `_reap_cancelled_task`) over a bare expression statement; do not \"fix\" by\n  skipping the await.\n- Implicit string concatenation in a list (CodeQL\n  `py/implicit-string-concatenation-in-list`): two adjacent string literals\n  inside a list/tuple display are indistinguishable from a **missing comma**,\n  so a long message split over two lines trips it. Do not silence it with an\n  explicit `+` — extract the text to a module constant and reference that. The\n  same implicit concatenation is fine in a parenthesised assignment, where no\n  comma could have been intended. Precedent:\n  `tools/system/python_execution_tool/__init__.py`\n  (`_RUNTIME_FACTS_ANTI_EXAMPLE`). Bites hardest in `use_cases` /\n  `anti_examples` / `examples` tool metadata, where entries are prose and\n  routinely exceed the 100-char line limit.\n- Mixed import styles (CodeQL `py/import-and-import-from`): importing one\n  module with both `import X as alias` and `from X import name` — even when\n  the `from` import is function-local — raises an alert. It usually happens\n  when appending to an existing file: **use the import style the file already\n  established** (an existing `import core.context_budget as budget` means new\n  code calls `budget.name`, not `from core.context_budget import name`).\n- Except block handles `BaseException` (code-quality): catch `Exception`, not\n  `BaseException`. Collecting request/transport failures in a test thread still\n  works — `requests.exceptions.ConnectTimeout` subclasses `Exception`. Catching\n  `BaseException` also swallows `KeyboardInterrupt` / `SystemExit`. Do not keep\n  `noqa: BLE001` to silence it.\n- Unused global variable (CodeQL / code-quality \"Unused global variable\"):\n  CodeQL often **does not credit cross-module imports** as a use of a module-\n  level constant. A `FOO = \"...\"` in `text.py` that is only read via\n  `from …text import FOO` in another file can still alert. Prefer keeping\n  related copy in a structure that is clearly used in the defining module\n  (e.g. a dict entry under `HANDOFF_GUIDANCE[\"database_query:\"]` with prefix\n  matching in the consumer), or co-locate the constant with its only reader.\n  Do **not** add a no-op self-reference or `# noqa` just to silence the alert.\n- Shared client state under concurrent turns: LLM clients are cached per role\n  (`get_llm`) and the gateway runs turns in parallel, so **one client instance\n  serves several in-flight requests**. An instance flag mutated inside an error\n  handler is then read by requests that were already built under the old value.\n  Branch on **what the current request carried**, not on the flag's present\n  value — capture the fact locally where the request is built\n  (`marked = strip_cache_markers(kwargs) != kwargs`) and consult that in the\n  `except`. Precedent: the prompt-cache fallback, where a first 400 cleared\n  `_cache_markers_enabled` and a second already-marked request skipped its\n  uncached retry and failed the turn. Test it by having the fake dependency\n  mutate the shared state *before* raising, which reproduces the race\n  deterministically without threads.\n- CI typecheck does **not** cover `tests/`: `make typecheck` runs mypy over `PYTHON_SOURCE_PATHS` (`config core gateway integrations platform surfaces tools`) only. Type errors in test files never fail CI, so do not assume a clean `make typecheck` means the tests you just wrote are type-clean — run mypy on the test path directly when it matters.\n\n"},"items":[{"name":"CLAUDE.md","path":"CLAUDE.md","title":"CLAUDE.md","content":"@AGENTS.md @CLAUDE_PERSONAL.md\n","category":"root","tokens":8},{"name":"AGENTS.md","path":"AGENTS.md","title":"AGENTS.md","content":"## OpenSRE Development Reference\n\n## Build and Run commands\n\n- Build `make install` (sets up the project environment via `uv sync` and installs this repo in editable mode)\n- Run **`uv run opensre …`** from the repo root while developing — preferred approach, uses this checkout even if another `opensre` is on your `PATH`.\n- Use **`uv run python …`** for any Python commands.\n\n## Code Style\n\n- Use strict typing, follow DRY principle\n- One clear purpose per file (separation of concerns)\n- Keep docstrings concise and contract-focused. Use one sentence for straightforward\n  APIs; add only non-obvious invariants, failure behavior, or layering constraints\n  callers must understand. Keep bug history and implementation narration in tests,\n  commits, or the PR description instead. Do not remove meaningful rationale merely\n  to shorten a docstring.\n- Use named constants for HTTP status codes (`http.HTTPStatus`, e.g.\n  `HTTPStatus.PAYMENT_REQUIRED`) in both source and tests — never hardcoded\n  numeric literals like `402`.\n- Env-var names and shared static constants live under `config/`, never inline\n  in a feature module or duplicated across files. Put them in a domain module\n  under `config/constants/` (e.g. `config/constants/billing.py`,\n  `config/constants/llm.py`) — a leaf that any layer can import without a cycle —\n  and re-export via `config/constants/__init__.py`. Do **not** define shared env\n  names in `config/config.py`: it imports `config.llm_auth.*`, so a name it and\n  one of those modules both need would force a cyclic import. Only a name used\n  solely inside `config/config.py` (nothing it imports needs it) may live there.\n- Do not keep compatibility-only forwarding modules after refactors. Once imports and tests\n  are migrated, remove the old module path in the same change and use one canonical import path.\n- Test fakes: never inline a lambda that builds an ad-hoc `type(...)` object (or\n  nests another lambda) into `monkeypatch.setattr` / `patch`. Extract a named\n  `def` and pass it — `type(...)` inside the helper body is fine:\n\n  ```python\n  def _build_harness() -> Any:\n      return type(\"H\", (), {\"resolve_env_variables\": lambda _self: None})()\n\n\n  monkeypatch.setattr(startup, \"_build_harness\", _build_harness)\n  ```\n\n  Trivial lambdas (`lambda **_kw: None`, `lambda: sentinel`) stay inline.\n  Precedent: `gateway/tests/runtime/test_startup.py` (`_StubHarness`),\n  `tests/cli/test_integrations_setup_github.py` (`_prompt_answering`).\n- Protocol methods you **add or change** use a **docstring-only body** — no\n  `...`, no `pass`, no `raise NotImplementedError`, and never a docstring *plus*\n  a trailing `...`/`pass`. Precedent (all fully compliant):\n  `platform/filestorage/ports.py`, `core/agent/loop_host.py`,\n  `gateway/core/runtime/sink_protocol.py`, `core/llm/types.py`.\n\n  ```python\n  class ObjectStore(Protocol):\n      def put_object(self, key: str, data: bytes) -> None:\n          \"\"\"Store ``data`` under ``key``.\"\"\"\n  ```\n\n  **The codebase is not yet compliant, and no tool will tell you.** An AST scan\n  of product code counts 89 docstring-only Protocol methods against **108\n  `raise NotImplementedError` stubs in 23 files** (`core/agent_harness/ports.py`,\n  `platform/harness_ports.py`, `gateway/core/storage/session/binding_store.py` and\n  others). Those are pre-existing and out of scope for a drive-by — do not\n  mass-convert them, and do not cite a file as precedent without checking it.\n\n  Enforcement is review-only:\n  - CodeQL `py/ineffectual-statement` catches **only** the bare `...` form.\n  - `pass` and `raise NotImplementedError` trip nothing.\n  - mypy exempts Protocol bodies from \"missing return\", so `pass` under a\n    `-> list[str]` signature passes `make typecheck`.\n\n### Tests (high-signal, not exhaustive)\n\nPrefer a **small suite that pins real failure modes** over broad line coverage.\nOne test per distinct bug class; do not multiply cases that exercise the same\nbranch with different literals.\n\n**Write / keep tests for:**\n\n- Security and authorization (allowlists, request-scoped authority, cross-actor\n  or cross-channel isolation).\n- Correctness under concurrency, crash, or shutdown (cursors, in-flight work,\n  ack vs replay, drain vs approval wait).\n- Package / transport borders that prevent silent coupling (no peer imports,\n  session key shape unique to the surface).\n- Regressions that already bit review or production (the P1 that forced a fix).\n\n**Skip or thin (unless they are the *only* coverage of a contract):**\n\n- Happy-path “mock was called once” wrappers (client send, init posts status).\n- Pure string / vocabulary tables when approve/deny/leave-open paths already\n  cover the helper.\n- Redundant success variants of an ack or dispatch path already covered by\n  fail / cancel / on_handled cases.\n- Defensive parse / “empty on fetch failure” edges that do not move durable\n  state.\n- Stand-in tests that restate control-flow intent without exercising the real\n  loop or wiring (e.g. “`create_task` does not block the creator”).\n\n\n### Docs under `docs/`\n\n`docs/` is user-facing. Test every sentence: *does this change what the reader\ndoes?* If not, cut it.\n\n- Cut vendor API endpoints (`getMe`), internal function names, which credential\n  tier a value lands in, and the bug a change fixed — that belongs in the PR\n  description or a module docstring.\n- Keep required vs optional, shortcuts that save real work, gotchas that are\n  invisible until they bite, and the exact commands and env vars they type.\n- Say \"a chat the bot was never added to fails during setup\", not \"`getChat`\n  returns `ok: false`\".\n\n### Performance (algorithms & data structures)\n\nApply on the **hot path** (per-request / per-iteration / per-tool-call); leave cold\ncode simple. Complexity must be deliberate — the simplest structure that meets the\nasymptotic need, and no more.\n\n- **Membership / dedup in a loop → `set`/`dict`, never `x in list`.** List `in` is\n  O(n); a set is O(1). (frozen dataclasses are hashable, so `set()` works on them.)\n- **Resolve once, reuse.** Don't re-scan for something already looked up — if you\n  fetched an object from a map, read its fields directly instead of a second linear\n  scan. Build an O(1) `{name: obj}` map instead of scanning a list by name.\n- **No `deepcopy` / `json.dumps` on the hot path.** If the result is invariant after\n  construction, compute it once (`functools.cached_property` / a stored field) and\n  treat it read-only. Verify no caller mutates a shared cached object first.\n- **Sort the light thing, once.** Sort keys/names (strings), not heavy objects, and\n  don't re-sort the same collection twice for two outputs.\n- **Right structure for the job:** `collections.deque` for queues/both-ends,\n  `heapq` for top-k, `bisect` for sorted search, `OrderedDict.move_to_end` /\n  `functools.lru_cache` for bounded caches, `\"\".join(parts)` never `+=` in a loop.\n- **Behavior-preserving refactors are TDD-guarded:** add a characterization test that\n  pins the observable behavior, confirm it passes on the *pre*-refactor code, then\n  keep it green through the change. Optimize only after; keep any benchmark in the PR.\n\n### File placement (all packages)\n\nWhen adding or changing behavior, put code in the **owning module first** — not the nearest\nshared file that already imports something similar.\n\n| Kind of file | Should contain | Should not contain |\n| --- | --- | --- |\n| **Orchestration** (`flow.py`, `controller.py`, `lifecycle.py`, `factory.py`) | Stage ordering, wiring, dispatch to specialists | Vendor/provider/domain logic, API clients, heavy UI |\n| **Shared UI / prompts** (`_ui.py`, `prompts.py`, generic `validation.py`) | Reusable prompts, tables, rendering, thin dispatch | Logic for one provider, integration, or vendor |\n| **Domain / provider / vendor module** (`providers/<name>.py`, `surfaces/cli/wizard/<name>.py`, `integrations/<vendor>/`) | All behavior specific to that provider, vendor, or feature area | Unrelated providers or cross-cutting orchestration |\n| **Registry / catalog** (`config.py`, `*_catalog.py`, `provider_registry.py`) | Metadata, defaults, discovery tables | Live API calls, onboarding prompts, retry loops |\n\n**Rules:**\n\n1. **Two or more functions for the same provider, vendor, or feature area** → add or extend\n   a dedicated module (or subpackage) for that area. Do not grow a shared orchestration or UI\n   file with provider-specific branches.\n2. **Before editing a shared file**, check for an existing sibling pattern in the same\n   package (`local_llm/`, `providers/azure_openai.py`, `integrations/<vendor>/tools/`, etc.).\n   Match that layout before inventing a new one.\n3. **Keep dispatch thin.** Shared entrypoints (`validate_provider_credentials`, `get_llm`,\n   slash-command handlers) should delegate in a few lines; implementation lives downstream.\n4. **Respect package boundaries** in [ARCHITECTURE.md](docs/ARCHITECTURE.md). Surfaces compose\n   lower tiers; `core/` and `integrations/` do not import from `surfaces/`.\n5. **Package-local detail** lives in that package's `AGENTS.md` when present (e.g.\n   [`surfaces/interactive_shell/AGENTS.md`](surfaces/interactive_shell/AGENTS.md),\n   [`core/llm/AGENTS.md`](core/llm/AGENTS.md)). Read it before structural changes in that tree.\n6. **Tool location** follows [docs/tool-placement-policy.md](docs/tool-placement-policy.md)\n   (vendor-specific vs `tools/system/` vs cross-vendor).\n\nIf a change would add a new provider-specific `if provider.value == ...` block to a file\nthat already serves multiple providers, stop and extract a dedicated module instead.\n\nBefore any push or PR creation follow [**CI.md**](CI.md) — lint, format, typecheck, and test commands all live there.\n\nWhen opening a PR, fill out the [**PR template**](.github/PULL_REQUEST_TEMPLATE.md) — it is not optional boilerplate; it has a required AI-usage disclosure section.\n\n## 1. Repo Map\n\n| Path                                          | What it does                                                                                                                                                                                                                                                                                                                           |\n| --------------------------------------------- | -------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |\n| `bootstrap/`                                  | Composition root: shared process boot (`process.py` — env, Sentry, adapters, capability warnings, LLM preload as an ordered `BootStep` table) and the registration steps themselves (`adapters.py`). Every host picks a `ProcessProfile` instead of writing its own boot order. The one package allowed to import `tools` and `integrations` together. |\n| `core/`                                       | Investigation orchestration, context assembly, the shared runtime tool-calling loop, and domain logic (state, types, correlation rules). Includes `core/tool_framework/` — the `BaseTool` base class, `@tool` decorator, registered-tool primitives, error telemetry, skill-guidance helpers, and shared payload utilities (`utils/`). |\n| `surfaces/cli/`                               | Command-line interface, onboarding wizard, local LLM helpers, and CLI tests support. Provider onboarding → `wizard/<provider>.py` (or `wizard/local_llm/`); new subcommands → `commands/<name>.py`. Runtime LLM wiring → [`core/llm/AGENTS.md`](core/llm/AGENTS.md).                                                                                                                                                                                                                                                   |\n| `surfaces/interactive_shell/`                 | Interactive terminal (REPL) loop, slash commands, chat/help surfaces, action-planning harness, and terminal UI.                                                                                                                                                                                                                        |\n| `integrations/`                               | Per-integration config normalization, verification, clients, helpers, store/catalog logic, the Hermes log pipeline, and per-vendor tool packages under `integrations/<vendor>/tools/`.                                                                                                                                                 |\n| `tools/`                                      | Tool registry, per-tool packages for cross-cutting tools that aren't vendor-specific (e.g. `tools/system/fleet_monitoring/`, `tools/system/watch_dog/`, `tools/system/sre_guidance_tool/`), and the interactive-shell action tools. Framework primitives (decorator, base class, utils) live in `core/tool_framework/`.                |\n| `config/`                                     | Shared constants, prompts, and UI theme.                                                                                                                                                                                                                                                                                               |\n| `tests/`                                      | Unit, integration, synthetic, deployment, e2e, chaos engineering, and support tests.                                                                                                                                                                                                                                                   |\n| `docs/`                                       | User-facing documentation, integration guides, and docs-site assets.                                                                                                                                                                                                                                                                   |\n| `.github/`                                    | CI workflows, issue templates, pull request template, and repository automation.                                                                                                                                                                                                                                                       |\n| `Dockerfile`                                  | Optional production container image (FastAPI health app via uvicorn).                                                                                                                                                                                                                                                                  |\n| `pyproject.toml`                              | Python project metadata, dependency configuration, tooling, and package settings.                                                                                                                                                                                                                                                      |\n| `Makefile`                                    | Canonical local automation for install, test, verify, deploy, and cleanup targets.                                                                                                                                                                                                                                                     |\n| `README.md`                                   | Product overview, install, quick start, high-level capabilities, and links to deeper docs.                                                                                                                                                                                                                                             |\n| `docs/DEVELOPMENT.md`                         | Contributor workflows: CI parity commands, dev container, benchmark, deployment, telemetry detail.                                                                                                                                                                                                                                     |\n| `docs/ARCHITECTURE.md`                        | Package architecture: the four-tier layer table, folder diagram, per-layer responsibilities, allowed cross-layer edges, and cross-layer flows.                                                                                                                                                                                         |\n| `docs/investigation-pipeline-architecture.md` | Investigation pipeline stages, ReAct loop control flow, and guardrails (tool cap, stagnation breaker, context budget), with diagrams.                                                                                                                                                                                                  |\n| `docs/investigation-tool-calling.md`          | Investigation ReAct tool schemas, LLM invoke payloads, and message shapes (all providers).                                                                                                                                                                                                                                             |\n| `docs/tool-placement-policy.md`               | Decision rule for where a tool lives: `integrations/<vendor>/tools/` vs. `tools/system/` vs. `tools/cross_vendor/` vs. `surfaces/shared/`.                                                                                                                                                                                             |\n| `docs/NAMING.md`                              | Naming conventions for `core/`: the glossary (State/Snapshot/RunInput/RunResult/Slice/Resources/Budget), the `{domain}_{role}.py` file rule, type naming (`Mixin` suffix, role-named Protocols, no package-name prefix), and anti-patterns.                                                                                            |\n| `SETUP.md`                                    | Machine setup (all platforms, Windows, MCP/OpenClaw, troubleshooting).                                                                                                                                                                                                                                                                 |\n| `CI.md`                                       | Mandatory pre-push checklist: lint, format, typecheck, tests — agents MUST follow before pushing.                                                                                                                                                                                                                                      |\n| `CONTRIBUTING.md`                             | Contribution workflow, branch/PR guidance, and quality expectations.                                                                                                                                                                                                                                                                   |\n\nMain packages one level deeper:\n\n- `platform/analytics/` — Analytics event plumbing and install helpers used by the onboarding flow.\n- `platform/auth/` — JWT and authentication helpers for local and hosted runtime access.\n- `surfaces/interactive_shell/` — REPL watchdog slash commands (`/watch`, `/watches`, `/unwatch`): PR demo steps live under **Interactive shell: REPL watchdog demo** in [docs/DEVELOPMENT.md](docs/DEVELOPMENT.md#interactive-shell-repl-watchdog-demo).\n- `config/constants/` — Shared prompt and other static constants.\n- `platform/deployment_ec2/` — EC2 AWS SDK primitives (`client`, `config`, EC2/IAM, SSM) and Telegram gateway AMI/systemd lifecycle (`telegram_gateway/`). Makefile: `make build-gateway-image`, `make deploy-gateway`.\n- `platform/guardrails/` — Guardrail rules, evaluation engine, audit helpers, and CLI bindings.\n- `platform/harness_ports.py` — Harness port layer (integration resolution, tool registry, investigation tools, GitHub repo scope). Real implementations are wired at startup via `integrations/harness_adapters.py` and `tools/harness_adapters.py` through `install_harness_ports()` in `surfaces/interactive_shell/ui/output/boundary.py`. See `core/agent_harness/AGENTS.md` for the import boundary.\n- `integrations/hermes/` — Hermes log tailing, incident classification, correlator, sinks, and investigation bridge.\n- `integrations/llm_cli/` — Subprocess-backed LLM CLIs (e.g. Codex). Extension guide: `integrations/llm_cli/AGENTS.md`.\n- `platform/masking/` — Masking utilities for redacting or normalizing sensitive content.\n- `tools/investigation/` — Composite investigation capability, public entrypoints, semantic stages, and reporting.\n- `core/llm/` — Hosted LLM provider clients, retry/schema helpers, and investigation tool-calling adapters.\n- `platform/sandbox/` — Sandboxed execution helpers for controlled runtime actions.\n- `core/state/` — Shared agent runtime envelope (`AgentState`), chat slice, investigation pipeline slice contracts, `EvidenceEntry`, state-update helpers, and pure defaults.\n- `core/domain/types/` — Shared typed contracts for evidence, retrieval, and tool-related payloads.\n- `tools/system/watch_dog/` — Watchdog feature: per-threshold alarm dispatch with cooldown (`--provider telegram|rocketchat`), sitting on top of `integrations/telegram/*` and `integrations/rocketchat/*`.\n- `gateway/web/webapp.py` — Web-facing health app served by the gateway daemon; the `opensre` CLI is `surfaces/cli/app.py`.\n\n## 2. Entry Points\n\n### Adding a Tool\n\nThe tool registry auto-discovers modules under `tools/`, so the normal path is to add one module or package there and let discovery pick it up. See [docs/adding-tools-and-integrations.md](docs/adding-tools-and-integrations.md) for the full file list and the detailed definition of done (package structure, contract/implementation rules, live-payload parsing, required docs/tests).\n\nSteps:\n\n1. Pick the simplest shape that fits the tool. Use a `BaseTool` subclass (from `core.tool_framework.base`) for richer behavior; use `@tool(...)` from `core.tool_framework.tool_decorator` for a lightweight function tool.\n2. Declare clear metadata: `name`, `description`, `source`, `input_schema`, and any `use_cases`, `requires`, `outputs`, or `retrieval_controls` you need.\n3. Before opening or approving the PR, follow [docs/adding-tools-and-integrations.md](docs/adding-tools-and-integrations.md).\n\n### Changing the investigation pipeline\n\nInvestigations are coordinated in `tools/investigation/lifecycle.py` and exposed via\n`tools/investigation/capability.py`. Semantic stages live under\n`tools/investigation/stages/`; reporting lives under\n`tools/investigation/reporting/`. See\n[docs/investigation-pipeline-architecture.md](docs/investigation-pipeline-architecture.md)\nfor the end-to-end stage/loop diagrams before making structural changes.\n\nFiles to touch:\n\n- `tools/investigation/lifecycle.py` for high-level stage ordering.\n- `core/state/` for shared agent state and investigation pipeline slice contracts\n  that cross stage boundaries.\n- `core/domain/` for pure investigation rules (alert source mapping, tool planning,\n  category alignment, correlation scoring).\n- `core/` for shared LLM runtime helpers (tool loop and LLM invoke error\n  classification).\n- `core/state/*.py` when adding or renaming persisted investigation fields\n  (update `AgentStateModel` and the matching slice).\n- `docs/` — update or add a page if the change introduces user-visible behavior or configuration.\n- `tests/` coverage for the affected CLI, synthetic, or integration paths.\n\nSteps:\n\n1. Keep each stage focused on one responsibility.\n2. Extend state models when new fields cross stage boundaries.\n3. Update tests that exercise `run_investigation` / streaming entry points.\n\n### Adding an Integration\n\nIntegration work usually spans config normalization, verification, integration-local clients/helpers, tools, docs, and tests. See [docs/adding-tools-and-integrations.md](docs/adding-tools-and-integrations.md) for the full file list, examples from the repo (Datadog, Grafana, Hermes), and the detailed definition of done (core completeness, investigation wiring, docs/tests, `make verify-integrations`, final demo gate).\n\nSteps:\n\n1. Add the integration config and normalization logic first so the rest of the stack can consume a consistent shape.\n2. Wire the tool layer after the config path is stable.\n3. Before opening or approving the PR, follow [docs/adding-tools-and-integrations.md](docs/adding-tools-and-integrations.md).\n\n## 3. Footguns (common mistakes to avoid)\n\n- Constant-condition toggles: never disable product logic with\n  `if False and …`, `if True or …`, or `if False:` to silence a failing test.\n  That hides real behavior (e.g. cancel short-circuit after action) and ships\n  as dead code. Keep the real condition and fix the test, or delete the branch.\n  Enforced by `tests/quality/test_no_constant_condition_toggles.py`.\n- No planning-stage fail-closed safeguard (v0.1): the interactive-shell action planner never denies a turn — do **not** reintroduce a planner denial, `mark_unhandled`, or the `UNHANDLED:` convention. Full rationale: [docs/interactive-shell-action-policy.md](docs/interactive-shell-action-policy.md); package rule: `surfaces/interactive_shell/AGENTS.md` (\"Action Selection And Execution\").\n- Docs navigation: Adding an `.mdx` file under `docs/` is not enough — Mintlify only shows pages listed in `docs/docs.json`. Forgetting the `pages` entry leaves the doc unreachable from the site sidebar.\n- Investigation tool schemas: draft-07 JSON Schema (e.g. `\"type\": [\"object\", \"null\"]`) can pass loose checks but fail the LLM API on first invoke because **all** available investigation tools are sent together. Normalize in the provider adapter and extend registry contract tests; see [docs/investigation-tool-calling.md](docs/investigation-tool-calling.md).\n- Action-agent path: do not implement regex/keyword/fuzzy intent routing or deterministic action bypasses around the action agent — including in the harness orchestrator / `SessionGoal` loop / evidence-tier policy. Intent belongs in the action turn (structured handoff tags such as `evidence_kind:…`, `session_goal:…`, `database_query:…`); hosts react to those tags or explicit APIs only. See `surfaces/interactive_shell/AGENTS.md` (\"Action Selection And Execution\") for the sanctioned literal-`/slash` exception, and `core/agent_harness/AGENTS.md`.\n- Information exposure through an exception (CWE-209 / CodeQL `py/stack-trace-exposure`): never send an exception's detail — `str(exc)`, `repr(exc)`, `traceback.format_exc()`, `exc.args`, provider/model/field internals — to an **external surface**. External surfaces are HTTP responses (`JSONResponse`/`HTTPException.detail` in `gateway/web/`) and chat gateway messages delivered to Slack/Telegram users (`OutputSink.render_error` on the gateway sinks). Log full detail server-side (`logger` + `capture_exception`) and return a generic message or `type(exc).__name__` only. The local CLI/terminal sink is **not** external — it may show detail. Redact at the sink/response boundary, not per call site, so the shared turn engine keeps detail for local dev.\n- Cyclic imports (CodeQL `py/cyclic-import`): CodeQL counts **function-local** and `TYPE_CHECKING` imports as part of a cycle, so making an import lazy does **not** clear the alert. Break the cycle structurally — move the shared symbol (type, exception, helper) into a **leaf** module both sides import, and never add a back-edge from a lower-level module up to a higher-level one. Precedent: `surfaces/cli/wizard/validation_result.py` and `surfaces/cli/llm_auth/persist.py` exist only to hold shared symbols so `validation` ↔ `azure_openai` and `_ui` → `service` stay acyclic.\n- CodeQL does not model `NoReturn`: it treats `pytest.skip`, `pytest.fail`, `sys.exit`, `typer.Exit` and custom raise-helpers as if they return, so any code after them looks reachable. Two alerts come from this — `py/uninitialized-local-variable` when a name is bound in `try` and the `except` only calls such a function, and unreachable-code when a `with` body ends in a bare `raise`. Do **not** silence with a comment: bind the name on every path CodeQL can see. Prefer a sentinel over exception control flow for ordinary \"not found\" — `next(iterable, None)` plus an explicit `if x is None:` guard, not `try: next(...) except StopIteration:`. `mypy` narrows correctly after the guard because it *does* honour `NoReturn`. For the bare-`raise` case, extract a `_raise()` helper.\n- Protocol stub bodies (CodeQL `py/ineffectual-statement`): a bare `...` on a\n  `Protocol` method is a valid PEP-544 idiom but trips CodeQL as a statement\n  with no effect. Do **not** write `def foo(self) -> T: ...`. Use a one-line\n  docstring as the only body (see Code Style above), and do not keep both a\n  docstring and a trailing `...`/`pass`. Prefer documenting the contract on the\n  port over silencing the alert with a comment. Note the scope: CodeQL catches\n  only `...`, so a clean scan does **not** mean the codebase follows the rule —\n  `pass` and `raise NotImplementedError` are invisible to it, and 108 of the\n  latter remain.\n- `py/ineffectual-statement` does **not** understand `await`: a bare\n  `await some_task` reads to CodeQL as a discarded expression. It is not — the\n  await is the side effect (e.g. reaping a cancelled task so `client.close()`\n  runs). Do **not** delete the await. Prefer a small helper that binds the\n  result (`_finished = await task` in `gateway/transports/discord/worker.py`\n  `_reap_cancelled_task`) over a bare expression statement; do not \"fix\" by\n  skipping the await.\n- Implicit string concatenation in a list (CodeQL\n  `py/implicit-string-concatenation-in-list`): two adjacent string literals\n  inside a list/tuple display are indistinguishable from a **missing comma**,\n  so a long message split over two lines trips it. Do not silence it with an\n  explicit `+` — extract the text to a module constant and reference that. The\n  same implicit concatenation is fine in a parenthesised assignment, where no\n  comma could have been intended. Precedent:\n  `tools/system/python_execution_tool/__init__.py`\n  (`_RUNTIME_FACTS_ANTI_EXAMPLE`). Bites hardest in `use_cases` /\n  `anti_examples` / `examples` tool metadata, where entries are prose and\n  routinely exceed the 100-char line limit.\n- Mixed import styles (CodeQL `py/import-and-import-from`): importing one\n  module with both `import X as alias` and `from X import name` — even when\n  the `from` import is function-local — raises an alert. It usually happens\n  when appending to an existing file: **use the import style the file already\n  established** (an existing `import core.context_budget as budget` means new\n  code calls `budget.name`, not `from core.context_budget import name`).\n- Except block handles `BaseException` (code-quality): catch `Exception`, not\n  `BaseException`. Collecting request/transport failures in a test thread still\n  works — `requests.exceptions.ConnectTimeout` subclasses `Exception`. Catching\n  `BaseException` also swallows `KeyboardInterrupt` / `SystemExit`. Do not keep\n  `noqa: BLE001` to silence it.\n- Unused global variable (CodeQL / code-quality \"Unused global variable\"):\n  CodeQL often **does not credit cross-module imports** as a use of a module-\n  level constant. A `FOO = \"...\"` in `text.py` that is only read via\n  `from …text import FOO` in another file can still alert. Prefer keeping\n  related copy in a structure that is clearly used in the defining module\n  (e.g. a dict entry under `HANDOFF_GUIDANCE[\"database_query:\"]` with prefix\n  matching in the consumer), or co-locate the constant with its only reader.\n  Do **not** add a no-op self-reference or `# noqa` just to silence the alert.\n- Shared client state under concurrent turns: LLM clients are cached per role\n  (`get_llm`) and the gateway runs turns in parallel, so **one client instance\n  serves several in-flight requests**. An instance flag mutated inside an error\n  handler is then read by requests that were already built under the old value.\n  Branch on **what the current request carried**, not on the flag's present\n  value — capture the fact locally where the request is built\n  (`marked = strip_cache_markers(kwargs) != kwargs`) and consult that in the\n  `except`. Precedent: the prompt-cache fallback, where a first 400 cleared\n  `_cache_markers_enabled` and a second already-marked request skipped its\n  uncached retry and failed the turn. Test it by having the fake dependency\n  mutate the shared state *before* raising, which reproduces the race\n  deterministically without threads.\n- CI typecheck does **not** cover `tests/`: `make typecheck` runs mypy over `PYTHON_SOURCE_PATHS` (`config core gateway integrations platform surfaces tools`) only. Type errors in test files never fail CI, so do not assume a clean `make typecheck` means the tests you just wrote are type-clean — run mypy on the test path directly when it matters.\n\n","category":"root","tokens":8391}]}