From 91608dd4083a9db964817897504a5fa1f4494697 Mon Sep 17 00:00:00 2001 From: Deep Kumar Singh Kushwah Date: Sun, 24 May 2026 01:08:15 +0530 Subject: [PATCH 1/2] fix(logging): allowlist rag_core/audit.py for RAG001 audit.py uses stdlib `logging.getLogger("rag.audit")` because rag_observability depends on rag_core.types, so any import from rag_core.audit into rag_observability.logging (directly or via the rag_core.logging shim) would create a circular import. This matches the policy described in CLAUDE.md. Also document the policy in docs/guides/logging-policy.md, including the full allowlist table and instructions for extending it. Co-Authored-By: Claude Opus 4.7 (1M context) --- docs/README.md | 6 ++++ docs/guides/logging-policy.md | 61 +++++++++++++++++++++++++++++++++++ scripts/check_logging.py | 4 +++ 3 files changed, 71 insertions(+) create mode 100644 docs/guides/logging-policy.md diff --git a/docs/README.md b/docs/README.md index 9c89ed8..1f03a21 100644 --- a/docs/README.md +++ b/docs/README.md @@ -9,6 +9,12 @@ | [eval-skeleton.md](architecture/eval-skeleton.md) | Eval framework architecture: golden-set schema, metric functions, RAGAS spike, `ragctl eval` CLI, extension points | | [iac.md](architecture/iac.md) | IaC overview: Terraform module design, Helm chart structure, dev/prod environments, extension points | +## guides/ + +| File | Description | +|------|-------------| +| [logging-policy.md](guides/logging-policy.md) | RAG001 policy: structured-logging requirement, allowlist, and how to extend it | + ## adr/ | File | Description | diff --git a/docs/guides/logging-policy.md b/docs/guides/logging-policy.md new file mode 100644 index 0000000..cc3a539 --- /dev/null +++ b/docs/guides/logging-policy.md @@ -0,0 +1,61 @@ +# Logging policy (RAG001) + +## Overview + +Every log record emitted by AgentContextOS must conform to the 7-field JSON +schema enforced by `rag_observability.logging`. To guarantee this, the project +bans direct use of `logging.getLogger()` in application code — a pre-commit +hook (`scripts/check_logging.py`) called **RAG001** fails the build if it is +called outside an explicit allowlist. + +## Usage + +Use the structured logger in all new code: + +```python +from rag_observability.logging import get_logger + +_log = get_logger(__name__) +_log.info("event.name", extra={"rag_tenant_id": tenant_id, ...}) +``` + +`rag_core.logging` re-exports the same symbols as a backwards-compatibility +shim — either import path is fine. + +Run the check manually: + +```bash +task lint:logging # via Taskfile +uv run python scripts/check_logging.py # direct +``` + +The hook also runs automatically through pre-commit on every staged commit. + +## Internals + +The check scans every tracked `.py` file under the repo root with the regex +`\blogging\.getLogger\s*\(` and reports each match as a `RAG001` violation. +Files in `_SKIP_DIRS` (`.venv`, `__pycache__`, `.git`, `.mypy_cache`, +`.ruff_cache`, `node_modules`) are skipped wholesale. Files in `_ALLOWLIST` +are permitted to call `logging.getLogger()` directly. + +### Allowlist + +| Path | Reason | +|------|--------| +| `packages/observability/src/rag_observability/logging.py` | The structured-logger implementation itself. | +| `packages/core/src/rag_core/logging.py` | Backwards-compat shim that re-exports the observability logger. | +| `packages/core/src/rag_core/audit.py` | Cannot import the structured logger without creating a circular import: `rag_observability` depends on `rag_core.types`, so any path from `rag_core.audit` → `rag_observability` → `rag_core` is a cycle. | +| `tests/logs/conftest.py` | Test fixtures that intentionally manipulate the `rag` logger hierarchy to assert schema conformance. | +| `scripts/check_logging.py` | This script — its pattern string contains the banned call as a literal. | + +## Extension points + +If you need to add a new allowlisted file, prefer fixing the underlying cause +instead — a circular import almost always points to a layering mistake, and +test scaffolding can usually live under `tests/logs/conftest.py`. When a new +entry is genuinely required: + +1. Add the path to `_ALLOWLIST` in [scripts/check_logging.py](../../scripts/check_logging.py). +2. Add a one-line comment above the entry explaining why the exception is necessary. +3. Update the allowlist table above in the same PR. diff --git a/scripts/check_logging.py b/scripts/check_logging.py index 45bd132..e98da23 100644 --- a/scripts/check_logging.py +++ b/scripts/check_logging.py @@ -27,6 +27,10 @@ Path("packages/observability/src/rag_observability/logging.py"), # Shim re-export — delegates to rag_observability.logging. Path("packages/core/src/rag_core/logging.py"), + # rag_core.audit cannot import rag_observability.logging (or the + # rag_core.logging shim that re-exports it) because rag_observability + # depends on rag_core.types — doing so would create a circular import. + Path("packages/core/src/rag_core/audit.py"), # Test infrastructure that intentionally manipulates the rag logger hierarchy. Path("tests/logs/conftest.py"), # This script — pattern string contains the banned call as a literal. From de6a4b87c7aa9ffe6353a14dfd96cc063ae5a39d Mon Sep 17 00:00:00 2001 From: Deep Kumar Singh Kushwah Date: Sun, 24 May 2026 01:14:22 +0530 Subject: [PATCH 2/2] ci: wire RAG001 logging check into ci.yml on all OSes MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit CLAUDE.md lists RAG001 (scripts/check_logging.py) as a blocking CI gate, but until now it only ran via pre-commit and `task lint:logging` — CI on GitHub never invoked it, which is how the audit.py violation fixed in PR #37 slipped to main. Add `uv run python scripts/check_logging.py` as a step in the existing `lint-test` matrix job so it runs on ubuntu-22.04, macos-14, and windows-latest alongside ruff and mypy. Co-Authored-By: Claude Opus 4.7 (1M context) --- .github/workflows/ci.yml | 3 +++ docs/guides/logging-policy.md | 5 ++++- 2 files changed, 7 insertions(+), 1 deletion(-) diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index a23c3e4..efd9b4f 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -75,6 +75,9 @@ jobs: - name: Ruff format check run: uv run ruff format --check . + - name: RAG001 — logging policy check + run: uv run python scripts/check_logging.py + - name: Mypy (strict) run: uv run mypy packages/ apps/gateway/ diff --git a/docs/guides/logging-policy.md b/docs/guides/logging-policy.md index cc3a539..438a0ec 100644 --- a/docs/guides/logging-policy.md +++ b/docs/guides/logging-policy.md @@ -29,7 +29,10 @@ task lint:logging # via Taskfile uv run python scripts/check_logging.py # direct ``` -The hook also runs automatically through pre-commit on every staged commit. +The hook also runs automatically through pre-commit on every staged commit, +and via the `lint-test` job in [.github/workflows/ci.yml](../../.github/workflows/ci.yml) +on every push and pull request — a non-zero exit fails the build on +ubuntu-22.04, macos-14, and windows-latest. ## Internals