From 82611a8fb81abe5ffa73fcf69fcd39c3ed7bac7f Mon Sep 17 00:00:00 2001 From: "copilot-swe-agent[bot]" <198982749+Copilot@users.noreply.github.com> Date: Tue, 15 Sep 2026 05:39:17 +0000 Subject: [PATCH 1/5] Initial plan From 3c6388d870da982a89b6ccfd27459a86b1c6d334 Mon Sep 17 00:00:00 2001 From: "copilot-swe-agent[bot]" <198982749+Copilot@users.noreply.github.com> Date: Tue, 15 Sep 2026 05:43:00 +0000 Subject: [PATCH 2/5] docs: add repo map and repo skills Co-authored-by: groupthinking <154503486+groupthinking@users.noreply.github.com> --- .github/skills/codebase-summary/SKILL.md | 51 +++++ .../github-actions-failure-debugging/SKILL.md | 57 +++++ README.md | 10 +- docs/REPO_MAP.md | 213 ++++++++++++++++++ tests/test_repo_assist_docs.py | 38 ++++ 5 files changed, 368 insertions(+), 1 deletion(-) create mode 100644 .github/skills/codebase-summary/SKILL.md create mode 100644 .github/skills/github-actions-failure-debugging/SKILL.md create mode 100644 docs/REPO_MAP.md create mode 100644 tests/test_repo_assist_docs.py diff --git a/.github/skills/codebase-summary/SKILL.md b/.github/skills/codebase-summary/SKILL.md new file mode 100644 index 0000000..d409999 --- /dev/null +++ b/.github/skills/codebase-summary/SKILL.md @@ -0,0 +1,51 @@ +--- +name: codebase-summary +description: Generate a repo-specific map of self-correcting-executor, including the tree, key dependencies, and MCP verification steps. Use this when asked to summarize the architecture or prepare an agent run for this repository. +--- + +When asked to summarize or orient an agent in this repository, use the current +repo structure instead of generic MCP examples. + +1. Start from `/home/runner/work/self-correcting-executor/self-correcting-executor`. +2. Build the summary around these directories: + - `agents/` + - `connectors/` + - `mcp_server/` + - `protocols/` + - `frontend/` + - `tests/` +3. Call out the primary dependency sources: + - `requirements.txt` + - `requirements-ci.txt` + - `requirements-test.txt` + - `frontend/package.json` +4. When MCP is relevant, explicitly distinguish: + - **host**: external Copilot/CLI/operator + - **client**: external MCP-aware caller + - **server**: the repo's implementations in `mcp_server/` and + `quantum_mcp_server/` +5. Use the live baseline verification sequence before claiming MCP is working: + + ```python + import asyncio + from mcp_server.main import MCPServer + + async def main(): + server = MCPServer() + await server.handle_request( + { + "jsonrpc": "2.0", + "id": 1, + "method": "initialize", + "params": {"clientInfo": {"name": "summary-skill", "version": "1.0"}}, + } + ) + await server.handle_request( + {"jsonrpc": "2.0", "id": 2, "method": "tools/list", "params": {}} + ) + + asyncio.run(main()) + ``` + +6. Point the reader to `docs/REPO_MAP.md` for the maintained tree, mermaid + diagram, dependency snapshot, workflow checklist, and CI/MCP notes. diff --git a/.github/skills/github-actions-failure-debugging/SKILL.md b/.github/skills/github-actions-failure-debugging/SKILL.md new file mode 100644 index 0000000..10dc739 --- /dev/null +++ b/.github/skills/github-actions-failure-debugging/SKILL.md @@ -0,0 +1,57 @@ +--- +name: github-actions-failure-debugging +description: Guide for debugging failing GitHub Actions workflows in self-correcting-executor. Use this when asked to investigate Python CI, Frontend CI, or PR workflow failures in this repository. +--- + +Use GitHub MCP tools first, then reproduce locally with the repo's existing +commands. + +1. List recent workflow runs for `groupthinking/self-correcting-executor`. + Focus on: + - `.github/workflows/python-ci.yml` + - `.github/workflows/frontend-ci.yml` +2. Get job logs for the failed run or failed jobs. +3. Interpret the result against this repo's workflow behavior: + - `python-ci.yml` runs `black --check .` + - `python-ci.yml` currently masks `flake8` and `pytest` failures with + `|| echo ...` and `continue-on-error`, so a green workflow does not always + mean imports/tests are healthy + - `frontend-ci.yml` runs `npm ci || npm install`, then lint/test/build from + `/frontend` +4. Reproduce only the relevant local command: + + ```bash + cd /home/runner/work/self-correcting-executor/self-correcting-executor + python -m pytest tests/ -q + cd /home/runner/work/self-correcting-executor/self-correcting-executor/frontend + npm run lint + npm run build + ``` + +5. If the failure touches MCP behavior, also run the repo's live MCP baseline: + + ```bash + cd /home/runner/work/self-correcting-executor/self-correcting-executor + python -m pytest tests/test_repo_assist_docs.py -q + python - <<'PY' + import asyncio + from mcp_server.main import MCPServer + + async def main(): + server = MCPServer() + response = await server.handle_request( + { + "jsonrpc": "2.0", + "id": 1, + "method": "initialize", + "params": {"clientInfo": {"name": "ci-debug", "version": "1.0"}}, + } + ) + print(response["result"]["serverInfo"]) + + asyncio.run(main()) + PY + ``` + +6. Keep fixes surgical. Do not "clean up" unrelated files just because the + workflow surfaced pre-existing noise. diff --git a/README.md b/README.md index 81997fc..ad4478d 100644 --- a/README.md +++ b/README.md @@ -33,4 +33,12 @@ Open http://localhost:8080/health to verify the API is running. ## Development -The workspace includes a **DevContainer** definition – simply open the folder in VS Code or Cursor and choose *Reopen in Container* to get an IDE connected to the running services. \ No newline at end of file +The workspace includes a **DevContainer** definition – simply open the folder in VS Code or Cursor and choose *Reopen in Container* to get an IDE connected to the running services. + +## Repo assist + +For a repo-specific map of the current tree, dependency snapshot, mermaid +architecture diagram, MCP capability-verification notes, and agent workflow +checklist, see +`/home/runner/work/self-correcting-executor/self-correcting-executor/docs/REPO_MAP.md` +(also tracked in the repository as [`docs/REPO_MAP.md`](docs/REPO_MAP.md)). \ No newline at end of file diff --git a/docs/REPO_MAP.md b/docs/REPO_MAP.md new file mode 100644 index 0000000..30a0c23 --- /dev/null +++ b/docs/REPO_MAP.md @@ -0,0 +1,213 @@ +# Repo Map, MCP Verification, and Agent Workflow Checklist + +This repository mixes agent orchestration, MCP servers, connectors, protocols, +and a small frontend. This document makes the issue's requested "repo assist" +material specific to `groupthinking/self-correcting-executor` so an agent can +map the repo, verify the MCP baseline live, and decide what to check before +commit/merge. + +## Repo tree (focused map) + +```text +self-correcting-executor/ +├── agents/ # A2A framework, executor, mutator, MCP integrations +│ ├── a2a_framework.py +│ ├── a2a_mcp_integration.py +│ ├── executor.py +│ ├── mutator.py +│ ├── specialized/ +│ └── unified/ +├── analyzers/ # Pattern analysis helpers +├── auth/ # Auth helpers +├── config/ # MCP and component configuration +├── connectors/ # D-Wave, GitHub, LLM, xAI, MCP base connectors +├── docs/ # Architecture, planning, and task guides +├── fabric/ # State continuity and integrated MCP fabric +├── frontend/ # Vite + React UI +├── llm/ # Continuous learning system +├── mcp_runtime_template_hg/ # SDK/API/CLI MCP runtime template +├── mcp_server/ # JSON-RPC MCP server + FastMCP-backed server +├── middleware/ # Security middleware +├── protocols/ # Executable protocols/tasks +├── quantum_mcp_server/ # Quantum-focused MCP server variant +├── scripts/ # Setup, compliance, cleanup, security scripts +├── test_data/ # Real input fixtures used by tests/scripts +├── tests/ # Pytest suite +├── ui/ # Additional UI assets and guide content +└── utils/ # Shared logging and DB tracking +``` + +## Dependency snapshot + +- Python runtime dependencies: `requirements.txt` currently pins **117** + packages, including `mcp`, `mcp-use`, FastAPI, D-Wave Ocean SDK, LangChain, + SQLAlchemy, Redis, and transformer-related packages. +- CI/test dependencies: `requirements-ci.txt` contains the lightweight + verification stack (`black`, `flake8`, `pytest`, `pytest-cov`, + `pytest-asyncio`). +- Frontend runtime dependencies: `frontend/package.json` currently declares + **7** runtime packages (`react`, `react-dom`, `@tanstack/react-query`, + `axios`, `framer-motion`, `lucide-react`, `three`) and **12** dev + dependencies for TypeScript, ESLint, and Vite. + +## Mermaid diagram + +```mermaid +flowchart TD + H[Host / Copilot / operator] + O[orchestrator.py
orchestrator_mapreduce.py] + + subgraph Servers[MCP servers] + M1[mcp_server/main.py
custom JSON-RPC MCPServer] + M2[mcp_server/real_mcp_server.py
FastMCP tool server] + M3[quantum_mcp_server/] + end + + subgraph Tools[Declared MCP tools] + T1[code_analyzer] + T2[protocol_validator] + T3[self_corrector] + end + + subgraph Connectors[Connectors] + C1[github_mcp_connector.py] + C2[dwave_quantum_connector.py] + C3[llm_connector.py] + C4[xai_connector.py] + end + + subgraph Tasks[protocols/*.py] + P1[data_processor] + P2[file_validator] + P3[database_health_check] + P4[system_monitor] + end + + H --> O + O --> M1 + O --> M2 + O --> M3 + M1 --> T1 + M1 --> T2 + M1 --> T3 + M2 --> C1 + M2 --> C2 + M2 --> C3 + M2 --> C4 + O --> Tasks + Tasks --> P1 + Tasks --> P2 + Tasks --> P3 + Tasks --> P4 +``` + +## Agentic workflow checklist for this repo + +- [x] **Agents**: `agents/a2a_framework.py`, `agents/a2a_mcp_integration.py`, + and `agents/unified/` contain agent coordination logic. +- [x] **Tools**: `mcp_server/main.py` declares `code_analyzer`, + `protocol_validator`, and `self_corrector`. +- [x] **MCP**: the repo is actively using MCP packages and MCP-shaped servers + (`mcp_server/main.py`, `mcp_server/real_mcp_server.py`, + `config/mcp_config.py`, `connectors/mcp_base.py`). +- [x] **Pull / push / request / commit / merge**: human workflows use GitHub + pull requests; cloud-agent workflows in this repo should publish progress + via PR updates instead of pushing directly from the sandbox. +- [x] **Issues**: link changes back to GitHub issues with `Closes #`. +- [x] **Code**: the main test entrypoint is `pytest` with `pytest.ini` + configured to use `tests/`. +- [x] **Deps**: Python dependencies live in `requirements*.txt`; frontend + dependencies live in `frontend/package.json`. +- [x] **Database**: DB-aware execution paths are visible in `utils/db_tracker.py` + and `protocols/database_health_check.py`. +- [x] **Actions**: `.github/workflows/python-ci.yml` and + `.github/workflows/frontend-ci.yml` define CI. +- [x] **Role assignment / security**: `auth/` and `middleware/` contain auth + and security logic. + +## MCP baseline: is this repo using MCP? + +Yes. The repository is using MCP in two forms: + +1. A custom JSON-RPC server in `/home/runner/work/self-correcting-executor/self-correcting-executor/mcp_server/main.py` + with explicit MCP methods such as `initialize`, `tools/list`, `tools/call`, + `resources/list`, and `resources/read`. +2. A FastMCP-based server in `/home/runner/work/self-correcting-executor/self-correcting-executor/mcp_server/real_mcp_server.py` + built on the `mcp` package. + +Compared with the MCP architecture/specification dated `2026-07-28`, this repo +already covers the **server** side baseline well enough to negotiate tools and +resources, but it does **not** implement a full host/client runtime in-repo. +That is acceptable for this codebase because GitHub Copilot/GitHub MCP acts as +the host environment around these servers. + +### Host / client / server mapping for this repo + +- **Host**: external agent host (for example Copilot, CLI, or another MCP-aware + operator) orchestrates requests and permissions. +- **Client**: the MCP-aware caller is external to this repo; this repo does not + ship a standalone client implementation. +- **Server**: this repo implements the server side in `mcp_server/` and + `quantum_mcp_server/`. + +## Live verification steps + +The minimum capability-negotiation proof for this repo is to exercise the +custom MCP server directly and confirm that `initialize` advertises +capabilities, then `tools/list` and `tools/call` work end-to-end. + +```bash +cd /home/runner/work/self-correcting-executor/self-correcting-executor +python - <<'PY' +import asyncio +import json +from mcp_server.main import MCPServer + +async def main(): + server = MCPServer() + for request in ( + { + "jsonrpc": "2.0", + "id": 1, + "method": "initialize", + "params": {"clientInfo": {"name": "repo-assist", "version": "1.0"}}, + }, + {"jsonrpc": "2.0", "id": 2, "method": "tools/list", "params": {}}, + { + "jsonrpc": "2.0", + "id": 3, + "method": "tools/call", + "params": { + "name": "code_analyzer", + "arguments": {"code": "def verify():\n return 1\n"}, + }, + }, + ): + print(json.dumps(await server.handle_request(request), indent=2)) + +asyncio.run(main()) +PY +``` + +Focused automated verification for the repo-assist additions: + +```bash +python -m pytest tests/test_repo_assist_docs.py -q +``` + +## CI baseline observed from GitHub Actions + +- The repo has active `Python CI` and `Frontend CI` workflows. +- The inspected recent `Python CI` job logs showed `black` passing. +- The same logs also showed that `flake8` findings and `pytest` collection + errors are currently masked by `|| echo ...` and `continue-on-error`, so the + workflow can report success even when Python imports/tests are not clean. + +## Skills added for this repo + +Project-scoped skills now live under `.github/skills/`: + +- `github-actions-failure-debugging`: tailored to the repo's Python/Frontend CI + workflows and GitHub MCP debugging flow. +- `codebase-summary`: tailored to this repo's tree mapping, dependency checks, + and MCP verification workflow. diff --git a/tests/test_repo_assist_docs.py b/tests/test_repo_assist_docs.py new file mode 100644 index 0000000..26eded7 --- /dev/null +++ b/tests/test_repo_assist_docs.py @@ -0,0 +1,38 @@ +from pathlib import Path + + +REPO_ROOT = Path(__file__).resolve().parents[1] + + +def test_repo_map_contains_requested_sections(): + repo_map = REPO_ROOT / "docs" / "REPO_MAP.md" + + assert repo_map.exists() + content = repo_map.read_text() + assert "## Repo tree (focused map)" in content + assert "## Dependency snapshot" in content + assert "## Mermaid diagram" in content + assert "## Agentic workflow checklist for this repo" in content + assert "## MCP baseline: is this repo using MCP?" in content + assert "## Live verification steps" in content + + +def test_repo_skills_are_present_and_repo_specific(): + skills = { + "github-actions-failure-debugging": ( + REPO_ROOT + / ".github" + / "skills" + / "github-actions-failure-debugging" + / "SKILL.md" + ), + "codebase-summary": ( + REPO_ROOT / ".github" / "skills" / "codebase-summary" / "SKILL.md" + ), + } + + for name, skill_path in skills.items(): + assert skill_path.exists(), f"Missing skill: {name}" + content = skill_path.read_text() + assert f"name: {name}" in content + assert "self-correcting-executor" in content From e317bfbe23f9728c61b27dd01b00308b6b358ebb Mon Sep 17 00:00:00 2001 From: "copilot-swe-agent[bot]" <198982749+Copilot@users.noreply.github.com> Date: Tue, 15 Sep 2026 05:43:58 +0000 Subject: [PATCH 3/5] test: format repo assist verification Co-authored-by: groupthinking <154503486+groupthinking@users.noreply.github.com> --- tests/test_repo_assist_docs.py | 1 - 1 file changed, 1 deletion(-) diff --git a/tests/test_repo_assist_docs.py b/tests/test_repo_assist_docs.py index 26eded7..3f2cd6a 100644 --- a/tests/test_repo_assist_docs.py +++ b/tests/test_repo_assist_docs.py @@ -1,6 +1,5 @@ from pathlib import Path - REPO_ROOT = Path(__file__).resolve().parents[1] From 3836cb0da46fa2c26c0725c1d04add440a8b79c4 Mon Sep 17 00:00:00 2001 From: Hayden <154503486+groupthinking@users.noreply.github.com> Date: Thu, 29 Oct 2026 05:27:15 -0500 Subject: [PATCH 4/5] fix: repair review findings and enforce tested CI behavior --- .github/dependabot.yml | 21 +- .github/skills/codebase-summary/SKILL.md | 30 +- .../github-actions-failure-debugging/SKILL.md | 38 +- .github/workflows/python-ci.yml | 3 +- README.md | 4 +- docs/REPO_MAP.md | 86 ++-- orchestrator_mapreduce.py | 99 ++-- requirements-ci.txt | 10 +- scripts/verify_mcp_baseline.py | 59 +++ tests/test_dependabot_test.py | 473 +++--------------- tests/test_mcp_baseline_verifier.py | 53 ++ tests/test_mcp_compliance.py | 106 ++-- 12 files changed, 349 insertions(+), 633 deletions(-) create mode 100644 scripts/verify_mcp_baseline.py create mode 100644 tests/test_mcp_baseline_verifier.py diff --git a/.github/dependabot.yml b/.github/dependabot.yml index adc776d..9b9da00 100644 --- a/.github/dependabot.yml +++ b/.github/dependabot.yml @@ -1,29 +1,14 @@ -groupthinking-patch-3 - # To get started with Dependabot version updates, you'll need to specify which - master -# package ecosystems to update and where the package manifests are located. -# Please see the documentation for all configuration options: -# https://docs.github.com/code-security/dependabot/dependabot-version-updates/configuration-options-for-the-dependabot.yml-file - version: 2 updates: -groupthinking-patch-3 - - package-ecosystem: "" # See documentation for possible values - - groupthinking-patch-2 - - package-ecosystem: "" # See documentation for possible values - - package-ecosystem: "pip" - directory: "/" # Location of package manifests + directory: "/" schedule: interval: "weekly" - package-ecosystem: "npm" - directory: "/frontend" # Location of package manifests + directory: "/frontend" schedule: interval: "weekly" - package-ecosystem: "docker" -master -master - directory: "/" # Location of package manifests + directory: "/" schedule: interval: "weekly" diff --git a/.github/skills/codebase-summary/SKILL.md b/.github/skills/codebase-summary/SKILL.md index d409999..d75a1dd 100644 --- a/.github/skills/codebase-summary/SKILL.md +++ b/.github/skills/codebase-summary/SKILL.md @@ -6,7 +6,7 @@ description: Generate a repo-specific map of self-correcting-executor, including When asked to summarize or orient an agent in this repository, use the current repo structure instead of generic MCP examples. -1. Start from `/home/runner/work/self-correcting-executor/self-correcting-executor`. +1. Start from the current repository root (`git rev-parse --show-toplevel`). 2. Build the summary around these directories: - `agents/` - `connectors/` @@ -24,28 +24,16 @@ repo structure instead of generic MCP examples. - **client**: external MCP-aware caller - **server**: the repo's implementations in `mcp_server/` and `quantum_mcp_server/` -5. Use the live baseline verification sequence before claiming MCP is working: +5. Use the in-process handler checks before reporting that baseline as verified: - ```python - import asyncio - from mcp_server.main import MCPServer - - async def main(): - server = MCPServer() - await server.handle_request( - { - "jsonrpc": "2.0", - "id": 1, - "method": "initialize", - "params": {"clientInfo": {"name": "summary-skill", "version": "1.0"}}, - } - ) - await server.handle_request( - {"jsonrpc": "2.0", "id": 2, "method": "tools/list", "params": {}} - ) - - asyncio.run(main()) + ```bash + cd "$(git rev-parse --show-toplevel)" + python -m scripts.verify_mcp_baseline + python -m pytest tests/test_mcp_baseline_verifier.py -q ``` + These checks exercise the custom handler in-process; they do not verify + an external MCP client/transport connection. + 6. Point the reader to `docs/REPO_MAP.md` for the maintained tree, mermaid diagram, dependency snapshot, workflow checklist, and CI/MCP notes. diff --git a/.github/skills/github-actions-failure-debugging/SKILL.md b/.github/skills/github-actions-failure-debugging/SKILL.md index 10dc739..e715fb6 100644 --- a/.github/skills/github-actions-failure-debugging/SKILL.md +++ b/.github/skills/github-actions-failure-debugging/SKILL.md @@ -13,45 +13,31 @@ commands. 2. Get job logs for the failed run or failed jobs. 3. Interpret the result against this repo's workflow behavior: - `python-ci.yml` runs `black --check .` - - `python-ci.yml` currently masks `flake8` and `pytest` failures with - `|| echo ...` and `continue-on-error`, so a green workflow does not always - mean imports/tests are healthy + - `python-ci.yml` runs pytest as a blocking step. Its existing `flake8` + step remains advisory via `|| echo ...` and `continue-on-error`, so a + green workflow does not establish a lint-clean repository. - `frontend-ci.yml` runs `npm ci || npm install`, then lint/test/build from `/frontend` 4. Reproduce only the relevant local command: ```bash - cd /home/runner/work/self-correcting-executor/self-correcting-executor + cd "$(git rev-parse --show-toplevel)" python -m pytest tests/ -q - cd /home/runner/work/self-correcting-executor/self-correcting-executor/frontend + cd "$(git rev-parse --show-toplevel)/frontend" npm run lint npm run build ``` -5. If the failure touches MCP behavior, also run the repo's live MCP baseline: +5. If the failure touches MCP behavior, also run the repo's in-process MCP handler checks: ```bash - cd /home/runner/work/self-correcting-executor/self-correcting-executor - python -m pytest tests/test_repo_assist_docs.py -q - python - <<'PY' - import asyncio - from mcp_server.main import MCPServer - - async def main(): - server = MCPServer() - response = await server.handle_request( - { - "jsonrpc": "2.0", - "id": 1, - "method": "initialize", - "params": {"clientInfo": {"name": "ci-debug", "version": "1.0"}}, - } - ) - print(response["result"]["serverInfo"]) - - asyncio.run(main()) - PY + cd "$(git rev-parse --show-toplevel)" + python -m scripts.verify_mcp_baseline + python -m pytest tests/test_mcp_baseline_verifier.py -q ``` + These checks exercise the custom handler in-process; they do not verify + an external MCP client/transport connection. + 6. Keep fixes surgical. Do not "clean up" unrelated files just because the workflow surfaced pre-existing noise. diff --git a/.github/workflows/python-ci.yml b/.github/workflows/python-ci.yml index c34e41b..81bd812 100644 --- a/.github/workflows/python-ci.yml +++ b/.github/workflows/python-ci.yml @@ -31,8 +31,7 @@ jobs: run: flake8 . || echo "Flake8 found issues but continuing CI" continue-on-error: true - name: Run tests with coverage - run: pytest --cov=. || echo "Tests failed but continuing CI" - continue-on-error: true + run: pytest --cov=. actions: runs-on: ubuntu-latest diff --git a/README.md b/README.md index ad4478d..c62cc81 100644 --- a/README.md +++ b/README.md @@ -39,6 +39,4 @@ The workspace includes a **DevContainer** definition – simply open the folder For a repo-specific map of the current tree, dependency snapshot, mermaid architecture diagram, MCP capability-verification notes, and agent workflow -checklist, see -`/home/runner/work/self-correcting-executor/self-correcting-executor/docs/REPO_MAP.md` -(also tracked in the repository as [`docs/REPO_MAP.md`](docs/REPO_MAP.md)). \ No newline at end of file +checklist, see [`docs/REPO_MAP.md`](docs/REPO_MAP.md). \ No newline at end of file diff --git a/docs/REPO_MAP.md b/docs/REPO_MAP.md index 30a0c23..2665bba 100644 --- a/docs/REPO_MAP.md +++ b/docs/REPO_MAP.md @@ -3,7 +3,7 @@ This repository mixes agent orchestration, MCP servers, connectors, protocols, and a small frontend. This document makes the issue's requested "repo assist" material specific to `groupthinking/self-correcting-executor` so an agent can -map the repo, verify the MCP baseline live, and decide what to check before +map the repo, verify the MCP handler in-process, and decide what to check before commit/merge. ## Repo tree (focused map) @@ -44,7 +44,8 @@ self-correcting-executor/ SQLAlchemy, Redis, and transformer-related packages. - CI/test dependencies: `requirements-ci.txt` contains the lightweight verification stack (`black`, `flake8`, `pytest`, `pytest-cov`, - `pytest-asyncio`). + `pytest-asyncio`) plus the MCP, NumPy, and PostgreSQL imports needed by + the collected tests. MCP/Pydantic match the production compatibility pair. - Frontend runtime dependencies: `frontend/package.json` currently declares **7** runtime packages (`react`, `react-dom`, `@tanstack/react-query`, `axios`, `framer-motion`, `lucide-react`, `three`) and **12** dev @@ -84,16 +85,11 @@ flowchart TD end H --> O - O --> M1 - O --> M2 - O --> M3 M1 --> T1 M1 --> T2 M1 --> T3 - M2 --> C1 M2 --> C2 - M2 --> C3 - M2 --> C4 + M2 --> P1 O --> Tasks Tasks --> P1 Tasks --> P2 @@ -101,6 +97,11 @@ flowchart TD Tasks --> P4 ``` +The MCP servers are separate entrypoints; no direct orchestrator-to-server +call is implied. `real_mcp_server.py` imports the D-Wave connector and the +data processor. Other connectors are shown as repository components, not +as dependencies of that server. + ## Agentic workflow checklist for this repo - [x] **Agents**: `agents/a2a_framework.py`, `agents/a2a_mcp_integration.py`, @@ -129,17 +130,16 @@ flowchart TD Yes. The repository is using MCP in two forms: -1. A custom JSON-RPC server in `/home/runner/work/self-correcting-executor/self-correcting-executor/mcp_server/main.py` +1. A custom JSON-RPC server in `mcp_server/main.py` with explicit MCP methods such as `initialize`, `tools/list`, `tools/call`, `resources/list`, and `resources/read`. -2. A FastMCP-based server in `/home/runner/work/self-correcting-executor/self-correcting-executor/mcp_server/real_mcp_server.py` +2. A FastMCP-based server in `mcp_server/real_mcp_server.py` built on the `mcp` package. -Compared with the MCP architecture/specification dated `2026-07-28`, this repo -already covers the **server** side baseline well enough to negotiate tools and -resources, but it does **not** implement a full host/client runtime in-repo. -That is acceptable for this codebase because GitHub Copilot/GitHub MCP acts as -the host environment around these servers. +The custom handler advertises protocol version `2024-11-05`. Direct calls +can verify its advertised tool and resource behavior, but do not establish +MCP specification compliance or successful transport negotiation with an +external host/client. Those require a separate integration test. ### Host / client / server mapping for this repo @@ -152,56 +152,28 @@ the host environment around these servers. ## Live verification steps -The minimum capability-negotiation proof for this repo is to exercise the -custom MCP server directly and confirm that `initialize` advertises -capabilities, then `tools/list` and `tools/call` work end-to-end. - -```bash -cd /home/runner/work/self-correcting-executor/self-correcting-executor -python - <<'PY' -import asyncio -import json -from mcp_server.main import MCPServer - -async def main(): - server = MCPServer() - for request in ( - { - "jsonrpc": "2.0", - "id": 1, - "method": "initialize", - "params": {"clientInfo": {"name": "repo-assist", "version": "1.0"}}, - }, - {"jsonrpc": "2.0", "id": 2, "method": "tools/list", "params": {}}, - { - "jsonrpc": "2.0", - "id": 3, - "method": "tools/call", - "params": { - "name": "code_analyzer", - "arguments": {"code": "def verify():\n return 1\n"}, - }, - }, - ): - print(json.dumps(await server.handle_request(request), indent=2)) - -asyncio.run(main()) -PY -``` +The focused verifier calls the custom handler in-process. It asserts the +initialize response, the advertised/listed tool names, and the actual +`code_analyzer` result. Any JSON-RPC error, missing capability or incorrect +analysis raises an exception and makes the command exit unsuccessfully. +This proves the handler behavior, not an external MCP transport connection. -Focused automated verification for the repo-assist additions: +From any directory inside the checkout: ```bash -python -m pytest tests/test_repo_assist_docs.py -q +cd "$(git rev-parse --show-toplevel)" +python -m scripts.verify_mcp_baseline +python -m pytest tests/test_repo_assist_docs.py tests/test_mcp_baseline_verifier.py -q ``` ## CI baseline observed from GitHub Actions - The repo has active `Python CI` and `Frontend CI` workflows. -- The inspected recent `Python CI` job logs showed `black` passing. -- The same logs also showed that `flake8` findings and `pytest` collection - errors are currently masked by `|| echo ...` and `continue-on-error`, so the - workflow can report success even when Python imports/tests are not clean. +- `Python CI` runs `black --check .` as a blocking step. Inspect the current + head's logs; a previous run's result is not evidence for a new commit. +- `pytest --cov=.` is blocking: collection failures and failed assertions fail + the matrix job. `flake8` remains advisory in the existing workflow, so a + green workflow alone does not prove the repository is free of lint findings. ## Skills added for this repo diff --git a/orchestrator_mapreduce.py b/orchestrator_mapreduce.py index f5f6111..d67093b 100644 --- a/orchestrator_mapreduce.py +++ b/orchestrator_mapreduce.py @@ -48,7 +48,6 @@ from agents.mutator import mutate_protocol from utils.tracker import track_outcome, get_protocol_stats - # ─── Data Models ─────────────────────────────────────────────── @@ -116,9 +115,7 @@ async def verify(self, job: OrchestratedJob) -> Dict[str, Any]: """ # Layer 1: Mechanical gate (fast pre-filter) total = len(job.subtasks) - succeeded = sum( - 1 for st in job.subtasks if st.status == TaskStatus.COMPLETED - ) + succeeded = sum(1 for st in job.subtasks if st.status == TaskStatus.COMPLETED) rate = succeeded / total if total > 0 else 0 if rate < self.threshold: @@ -127,8 +124,7 @@ async def verify(self, job: OrchestratedJob) -> Dict[str, Any]: "layer": "mechanical", "success_rate": rate, "reason": ( - f"Success rate {rate:.0%} below " - f"{self.threshold:.0%} threshold" + f"Success rate {rate:.0%} below " f"{self.threshold:.0%} threshold" ), } @@ -139,7 +135,11 @@ async def verify(self, job: OrchestratedJob) -> Dict[str, Any]: except Exception as e: # If LLM is unavailable, fall back to mechanical pass log(f"⚠️ Semantic gate unavailable ({e}), using mechanical only") - return {"passed": True, "layer": "mechanical_fallback", "success_rate": rate} + return { + "passed": True, + "layer": "mechanical_fallback", + "success_rate": rate, + } async def _llm_evaluate(self, job: OrchestratedJob) -> Dict[str, Any]: """Call the LLM to semantically evaluate the job output.""" @@ -209,12 +209,8 @@ class StateStore: """ def __init__(self, db_path: Optional[str] = None, md_path: Optional[str] = None): - self.db_path = db_path or str( - Path(__file__).parent / "loop_state.db" - ) - self.md_path = md_path or str( - Path(__file__).parent / "STATE.md" - ) + self.db_path = db_path or str(Path(__file__).parent / "loop_state.db") + self.md_path = md_path or str(Path(__file__).parent / "STATE.md") self._init_db() def _init_db(self): @@ -261,12 +257,8 @@ def persist_job(self, job: OrchestratedJob): conn = sqlite3.connect(self.db_path) now = datetime.now(timezone.utc).isoformat() - succeeded = sum( - 1 for st in job.subtasks if st.status == TaskStatus.COMPLETED - ) - failed = sum( - 1 for st in job.subtasks if st.status == TaskStatus.FAILED - ) + succeeded = sum(1 for st in job.subtasks if st.status == TaskStatus.COMPLETED) + failed = sum(1 for st in job.subtasks if st.status == TaskStatus.FAILED) conn.execute( """INSERT OR REPLACE INTO jobs @@ -305,12 +297,16 @@ def persist_job(self, job: OrchestratedJob): st.error, st.attempts, ( - datetime.fromtimestamp(st.started_at, tz=timezone.utc).isoformat() + datetime.fromtimestamp( + st.started_at, tz=timezone.utc + ).isoformat() if st.started_at else None ), ( - datetime.fromtimestamp(st.completed_at, tz=timezone.utc).isoformat() + datetime.fromtimestamp( + st.completed_at, tz=timezone.utc + ).isoformat() if st.completed_at else None ), @@ -322,9 +318,7 @@ def persist_job(self, job: OrchestratedJob): def persist_markdown(self, job: OrchestratedJob): """Append a human-readable summary to STATE.md.""" - succeeded = sum( - 1 for st in job.subtasks if st.status == TaskStatus.COMPLETED - ) + succeeded = sum(1 for st in job.subtasks if st.status == TaskStatus.COMPLETED) entry = ( f"\n## Job: {job.id}\n" f"- **Intent:** {job.intent}\n" @@ -458,7 +452,9 @@ async def _mutate_strategy( subtask.inputs.update(strategy_hints) - log(f" → Tier 2 (Strategy Mutation): Injected strategy hints for '{protocol_name}'") + log( + f" → Tier 2 (Strategy Mutation): Injected strategy hints for '{protocol_name}'" + ) return {"tier": 2, "strategy_note": strategy_hints["_instruction"]} async def _mutate_code_sandboxed( @@ -470,12 +466,12 @@ async def _mutate_code_sandboxed( The mutation is tested in a sandboxed dry-run before being applied. """ # Perform the mutation via the existing mutator - mutated = await loop.run_in_executor( - None, mutate_protocol, protocol_name - ) + mutated = await loop.run_in_executor(None, mutate_protocol, protocol_name) if not mutated: - log(f" → Tier 3 (Code Mutation): mutate_protocol returned None for '{protocol_name}'") + log( + f" → Tier 3 (Code Mutation): mutate_protocol returned None for '{protocol_name}'" + ) return {"tier": 3, "code_mutated": False, "reason": "Mutator returned None"} # Sandbox test: attempt to load and validate the mutated protocol @@ -486,7 +482,9 @@ async def _mutate_code_sandboxed( None, load_protocol, protocol_name ) if test_protocol and callable(test_protocol.get("task")): - log(f" → Tier 3 (Code Mutation): Mutation applied and validated for '{protocol_name}'") + log( + f" → Tier 3 (Code Mutation): Mutation applied and validated for '{protocol_name}'" + ) return {"tier": 3, "code_mutated": True} else: log(f" → Tier 3 (Code Mutation): Mutated protocol failed validation") @@ -514,9 +512,7 @@ async def escalate(self, job: OrchestratedJob, reason: str): Post a structured failure alert to Slack. Includes job ID, intent, failure reason, and failed subtask details. """ - failed_subtasks = [ - st for st in job.subtasks if st.status == TaskStatus.FAILED - ] + failed_subtasks = [st for st in job.subtasks if st.status == TaskStatus.FAILED] failed_details = "\n".join( f" • `{st.protocol}` — {st.error or 'unknown error'}" for st in failed_subtasks[:5] @@ -535,9 +531,7 @@ async def escalate(self, job: OrchestratedJob, reason: str): loop = asyncio.get_running_loop() try: - await loop.run_in_executor( - None, self._post_to_slack, message - ) + await loop.run_in_executor(None, self._post_to_slack, message) log(f"📨 Escalation posted to Slack {self.channel}") except Exception as e: # If Slack fails, log locally — never let escalation failure crash the system @@ -684,9 +678,7 @@ async def _execute_one(subtask: SubTask): try: result = await asyncio.wait_for( - self._run_protocol_isolated( - subtask.protocol, subtask.inputs - ), + self._run_protocol_isolated(subtask.protocol, subtask.inputs), timeout=self.subtask_timeout, ) subtask.result = result @@ -727,9 +719,7 @@ async def _execute_one(subtask: SubTask): return_exceptions=True, ) - completed = sum( - 1 for st in job.subtasks if st.status == TaskStatus.COMPLETED - ) + completed = sum(1 for st in job.subtasks if st.status == TaskStatus.COMPLETED) failed = sum(1 for st in job.subtasks if st.status == TaskStatus.FAILED) log(f"📊 MAP complete: {completed} succeeded, {failed} failed") @@ -760,15 +750,16 @@ async def reduce_verify(self, job: OrchestratedJob) -> OrchestratedJob: log(f"✅ REDUCE: Verification PASSED for job '{job.intent}'") else: job.verification_passed = False - log(f"❌ REDUCE: Verification FAILED — {verification.get('reason', 'unknown')}") + log( + f"❌ REDUCE: Verification FAILED — {verification.get('reason', 'unknown')}" + ) # Step 3: Self-Correct with safe mutation hierarchy while True: retryable = [ st for st in job.subtasks - if st.status == TaskStatus.FAILED - and st.attempts < st.max_attempts + if st.status == TaskStatus.FAILED and st.attempts < st.max_attempts ] if not retryable: @@ -797,11 +788,7 @@ async def reduce_verify(self, job: OrchestratedJob) -> OrchestratedJob: # Re-verify after retry job.reduced_result = await self.reducer( [st.result for st in job.subtasks if st.result], - [ - st - for st in job.subtasks - if st.status == TaskStatus.FAILED - ], + [st for st in job.subtasks if st.status == TaskStatus.FAILED], ) job.attempt_count += 1 re_verification = await self.verification_gate(job) @@ -875,9 +862,7 @@ async def _retry_subtask(self, subtask: SubTask): subtask.started_at = time.time() try: result = await asyncio.wait_for( - self._run_protocol_isolated( - subtask.protocol, subtask.inputs - ), + self._run_protocol_isolated(subtask.protocol, subtask.inputs), timeout=self.subtask_timeout, ) subtask.result = result @@ -903,9 +888,7 @@ async def _persist_state(self, job: OrchestratedJob): def _prune_job_history(self): """Prevent unbounded memory growth from stored jobs.""" if len(self.jobs) > self.max_job_history: - sorted_jobs = sorted( - self.jobs.items(), key=lambda x: x[1].created_at - ) + sorted_jobs = sorted(self.jobs.items(), key=lambda x: x[1].created_at) excess = len(self.jobs) - self.max_job_history for job_id, _ in sorted_jobs[:excess]: del self.jobs[job_id] @@ -918,9 +901,7 @@ async def _default_gate(self, job: OrchestratedJob) -> Dict[str, Any]: Used only when use_semantic_gate=False and no custom gate provided. """ total = len(job.subtasks) - succeeded = sum( - 1 for st in job.subtasks if st.status == TaskStatus.COMPLETED - ) + succeeded = sum(1 for st in job.subtasks if st.status == TaskStatus.COMPLETED) rate = succeeded / total if total > 0 else 0 if rate >= 0.8: diff --git a/requirements-ci.txt b/requirements-ci.txt index 2cb9651..d0a2908 100644 --- a/requirements-ci.txt +++ b/requirements-ci.txt @@ -1,7 +1,8 @@ # Minimal requirements for CI/CD pipeline # Core dependencies aiohttp>=3.8.0 -pydantic>=2.0.0 +# Match the production MCP SDK/Pydantic compatibility pair. +pydantic==2.11.7 fastapi>=0.100.0 uvicorn>=0.20.0 @@ -15,4 +16,9 @@ pytest-cov>=4.0.0 # Basic utilities requests>=2.28.0 python-dotenv>=1.0.0 -PyYAML>=6.0.0 \ No newline at end of file +PyYAML>=6.0.0 +# Imports exercised by the real MCP and database unit tests. +mcp==1.9.4 +numpy>=1.24,<3 +psycopg2-binary>=2.9.9 +packaging>=23.0 diff --git a/scripts/verify_mcp_baseline.py b/scripts/verify_mcp_baseline.py new file mode 100644 index 0000000..4321ac9 --- /dev/null +++ b/scripts/verify_mcp_baseline.py @@ -0,0 +1,59 @@ +"""Verify the custom MCP handler in-process; no network transport is tested.""" + +import asyncio +import json + +from mcp_server.main import MCPServer, MCP_VERSION + + +async def verify_baseline(server=None): + """Raise on RPC errors, missing capabilities, or incorrect analysis.""" + server = server if server is not None else MCPServer() + + async def request(request_id, method, params): + response = await server.handle_request( + {"jsonrpc": "2.0", "id": request_id, "method": method, "params": params} + ) + if response.get("error") is not None or "result" not in response: + raise RuntimeError(f"{method} failed: {response}") + if response.get("jsonrpc") != "2.0" or response.get("id") != request_id: + raise RuntimeError(f"{method} returned an invalid response: {response}") + return response["result"] + + initialized = await request( + 1, "initialize", {"clientInfo": {"name": "repo-assist", "version": "1.0"}} + ) + if initialized["serverInfo"]["mcpVersion"] != MCP_VERSION: + raise RuntimeError("initialize returned an unexpected protocol version") + expected_tools = {"code_analyzer", "protocol_validator", "self_corrector"} + advertised_tools = {tool["name"] for tool in initialized["capabilities"]["tools"]} + if not expected_tools <= advertised_tools: + raise RuntimeError("initialize did not advertise the expected tools") + if not initialized["capabilities"]["resources"]: + raise RuntimeError("initialize did not advertise resources") + listed = await request(2, "tools/list", {}) + if {tool["name"] for tool in listed["tools"]} != advertised_tools: + raise RuntimeError("tools/list differs from advertised capabilities") + called = await request( + 3, + "tools/call", + { + "name": "code_analyzer", + "arguments": {"code": "def verify():\n return 1\n"}, + }, + ) + if called["tool"] != "code_analyzer": + raise RuntimeError("tools/call returned the wrong tool") + analysis = json.loads(called["result"]["content"][0]["text"]) + if analysis != { + "lines_of_code": 2, + "functions": 1, + "complexity": 1, + "imports": [], + }: + raise RuntimeError(f"code_analyzer returned unexpected analysis: {analysis}") + return {"serverInfo": initialized["serverInfo"], "analysis": analysis} + + +if __name__ == "__main__": + print(json.dumps(asyncio.run(verify_baseline()), indent=2)) diff --git a/tests/test_dependabot_test.py b/tests/test_dependabot_test.py index bfc326f..aa7ccb3 100644 --- a/tests/test_dependabot_test.py +++ b/tests/test_dependabot_test.py @@ -1,412 +1,67 @@ -import os -import sys -from io import StringIO -from unittest.mock import mock_open, patch +"""Regression checks for the actual Dependabot and CI configuration. -import pytest - -# Add the .github directory to the path to import the module -sys.path.insert(0, os.path.join(os.path.dirname(__file__), "..", ".github")) - -try: - import dependabot_test -except ImportError: - # If the module doesn't exist or has import issues, we'll handle it - dependabot_test = None - - -class TestDependabotTest: - """Test suite for dependabot_test.py module""" - - def test_module_imports_successfully(self): - """Test that the dependabot_test module can be imported without errors""" - assert ( - dependabot_test is not None - ), "dependabot_test module should be importable" - - @pytest.fixture - def mock_file_system(self): - """Fixture for mocking file system operations""" - with patch("builtins.open", mock_open()) as mock_file: - yield mock_file - - @pytest.fixture - def mock_subprocess(self): - """Fixture for mocking subprocess operations""" - with patch("subprocess.run") as mock_run: - mock_run.return_value.returncode = 0 - mock_run.return_value.stdout = "" - mock_run.return_value.stderr = "" - yield mock_run - - def test_function_existence_and_callability(self): - """Test that expected functions exist and are callable""" - if dependabot_test is None: - pytest.skip("dependabot_test module not available") - - functions = [ - getattr(dependabot_test, name) - for name in dir(dependabot_test) - if callable(getattr(dependabot_test, name)) and not name.startswith("_") - ] - assert functions, "Module should contain at least one function" - - for func in functions: - assert callable(func), f"Function {func.__name__} should be callable" - - def test_main_function_exists(self): - """Test that main function exists if the module is meant to be executable""" - if dependabot_test is None: - pytest.skip("dependabot_test module not available") - - if hasattr(dependabot_test, "main"): - assert callable(dependabot_test.main), "main function should be callable" - - @patch("sys.argv", ["dependabot_test.py"]) - def test_script_execution_with_no_args(self): - """Test script execution with no arguments""" - if dependabot_test is None: - pytest.skip("dependabot_test module not available") - - if hasattr(dependabot_test, "main"): - try: - dependabot_test.main() - except SystemExit: - pass - except Exception as e: - pytest.fail(f"main() should not raise unexpected exception: {e}") - - @patch("sys.argv", ["dependabot_test.py", "--help"]) - def test_script_execution_with_help_flag(self): - """Test script execution with help flag""" - if dependabot_test is None: - pytest.skip("dependabot_test module not available") - - if hasattr(dependabot_test, "main"): - with patch("sys.stdout", new_callable=StringIO) as mock_stdout: - try: - dependabot_test.main() - except SystemExit: - pass - output = mock_stdout.getvalue() - assert output is not None - - def test_module_constants_and_variables(self): - """Test module-level constants and variables""" - if dependabot_test is None: - pytest.skip("dependabot_test module not available") - - module_attrs = dir(dependabot_test) - assert "__name__" in module_attrs - assert "__doc__" in module_attrs - - if hasattr(dependabot_test, "__version__"): - version = dependabot_test.__version__ - assert isinstance(version, str) - assert version - - def test_error_handling_with_invalid_input(self): - """Test error handling with various invalid inputs""" - if dependabot_test is None: - pytest.skip("dependabot_test module not available") - - functions = [ - getattr(dependabot_test, name) - for name in dir(dependabot_test) - if callable(getattr(dependabot_test, name)) and not name.startswith("_") - ] - - for func in functions: - try: - func(None) - except (TypeError, ValueError, AttributeError): - pass - except Exception as e: - print(f"Unexpected exception in {func.__name__}: {e}") - - def test_file_operations_mocked(self, mock_file_system): - """Test file operations with mocked file system""" - if dependabot_test is None: - pytest.skip("dependabot_test module not available") - - mock_file_system.return_value.read.return_value = "test content" - functions = [ - getattr(dependabot_test, name) - for name in dir(dependabot_test) - if callable(getattr(dependabot_test, name)) and not name.startswith("_") - ] - - for func in functions: - try: - func("test_file.txt") - except (TypeError, FileNotFoundError): - pass - except Exception as e: - print(f"Unexpected exception in {func.__name__}: {e}") - - def test_subprocess_operations_mocked(self, mock_subprocess): - """Test subprocess operations with mocked subprocess""" - if dependabot_test is None: - pytest.skip("dependabot_test module not available") - - functions = [ - getattr(dependabot_test, name) - for name in dir(dependabot_test) - if callable(getattr(dependabot_test, name)) and not name.startswith("_") - ] - - for func in functions: - try: - func() - except TypeError: - pass - except Exception as e: - print(f"Unexpected exception in {func.__name__}: {e}") - - def test_environment_variable_handling(self): - """Test handling of environment variables""" - if dependabot_test is None: - pytest.skip("dependabot_test module not available") - - test_env_vars = { - "GITHUB_TOKEN": "test_token", - "GITHUB_REPOSITORY": "test/repo", - "GITHUB_WORKSPACE": "/tmp/workspace", - } - - with patch.dict(os.environ, test_env_vars): - functions = [ - getattr(dependabot_test, name) - for name in dir(dependabot_test) - if callable(getattr(dependabot_test, name)) and not name.startswith("_") - ] - - for func in functions: - try: - func() - except Exception as e: - print(f"Function {func.__name__} with env vars: {e}") - - def test_github_api_interaction_mocked(self): - """Test GitHub API interactions with mocked responses""" - if dependabot_test is None: - pytest.skip("dependabot_test module not available") - - with patch("requests.get") as mock_get: - mock_get.return_value.status_code = 200 - mock_get.return_value.json.return_value = {"test": "data"} - - functions = [ - getattr(dependabot_test, name) - for name in dir(dependabot_test) - if callable(getattr(dependabot_test, name)) and not name.startswith("_") - ] - - for func in functions: - try: - func() - except Exception as e: - print(f"Function {func.__name__} with mocked HTTP: {e}") +The repository has no dependabot_test.py runtime module. Validate the shipped +configuration and the CI dependency set instead of importing a nonexistent helper. +""" - def test_edge_cases_and_boundary_conditions(self): - """Test edge cases and boundary conditions""" - if dependabot_test is None: - pytest.skip("dependabot_test module not available") +from pathlib import Path - edge_cases = [ - "", - " ", - "very_long_string_" * 100, - "special!@#$%^&*()chars", - None, - [], - {}, - 0, - -1, - ] - - functions = [ - getattr(dependabot_test, name) - for name in dir(dependabot_test) - if callable(getattr(dependabot_test, name)) and not name.startswith("_") - ] - - for func in functions: - for edge_case in edge_cases: - try: - func(edge_case) - except (TypeError, ValueError, AttributeError): - pass - except Exception as e: - print( - f"Unexpected exception in {func.__name__} with {edge_case}: {e}" - ) - - def test_concurrent_execution(self): - """Test concurrent execution safety""" - if dependabot_test is None: - pytest.skip("dependabot_test module not available") - - import threading - - functions = [ - getattr(dependabot_test, name) - for name in dir(dependabot_test) - if callable(getattr(dependabot_test, name)) and not name.startswith("_") - ] - - if not functions: - return - - results = [] - errors = [] - - def run_function(): - try: - result = functions[0]() - results.append(result) - except Exception as e: - errors.append(e) - - threads = [threading.Thread(target=run_function) for _ in range(5)] - for thread in threads: - thread.start() - for thread in threads: - thread.join(timeout=5) - - assert not any("thread" in str(e).lower() for e in errors) - - def test_memory_usage_and_cleanup(self): - """Test memory usage and proper cleanup""" - if dependabot_test is None: - pytest.skip("dependabot_test module not available") - - import gc - - functions = [ - getattr(dependabot_test, name) - for name in dir(dependabot_test) - if callable(getattr(dependabot_test, name)) and not name.startswith("_") - ] - - initial_objects = len(gc.get_objects()) - for func in functions: - try: - func() - except Exception: - pass - - gc.collect() - final_objects = len(gc.get_objects()) - assert final_objects < initial_objects * 2, "Potential memory leak detected" - - def test_logging_and_output(self): - """Test logging and output functionality""" - if dependabot_test is None: - pytest.skip("dependabot_test module not available") - - with patch("sys.stdout", new_callable=StringIO) as mock_stdout, patch( - "sys.stderr", new_callable=StringIO - ) as mock_stderr: - functions = [ - getattr(dependabot_test, name) - for name in dir(dependabot_test) - if callable(getattr(dependabot_test, name)) and not name.startswith("_") - ] - - for func in functions: - try: - func() - except Exception: - pass - - stdout_output = mock_stdout.getvalue() - stderr_output = mock_stderr.getvalue() - assert isinstance(stdout_output, str) - assert isinstance(stderr_output, str) - - def test_configuration_and_settings(self): - """Test configuration and settings handling""" - if dependabot_test is None: - pytest.skip("dependabot_test module not available") - - test_configs = [{}, {"key": "value"}, {"nested": {"key": "value"}}] - - functions = [ - getattr(dependabot_test, name) - for name in dir(dependabot_test) - if callable(getattr(dependabot_test, name)) and not name.startswith("_") - ] - - for config in test_configs: - for func in functions: - try: - func(config) - except (TypeError, KeyError, AttributeError): - pass - except Exception as e: - print( - f"Unexpected exception in {func.__name__} with config {config}: {e}" - ) - - -class TestDependabotTestIntegration: - """Integration tests for dependabot_test.py in GitHub Actions context""" - - def test_github_actions_environment(self): - """Test behavior in GitHub Actions environment""" - if dependabot_test is None: - pytest.skip("dependabot_test module not available") - - github_env = { - "GITHUB_ACTIONS": "true", - "GITHUB_WORKFLOW": "test", - "GITHUB_RUN_ID": "123456", - "GITHUB_RUN_NUMBER": "42", - "GITHUB_ACTOR": "dependabot[bot]", - "GITHUB_REPOSITORY": "test/repo", - "GITHUB_EVENT_NAME": "pull_request", - "GITHUB_SHA": "abc123", - "GITHUB_REF": "refs/heads/dependabot/npm_and_yarn/test-1.0.0", - } - - with patch.dict(os.environ, github_env): - functions = [ - getattr(dependabot_test, name) - for name in dir(dependabot_test) - if callable(getattr(dependabot_test, name)) and not name.startswith("_") - ] - - for func in functions: - try: - func() - except Exception as e: - print(f"Function {func.__name__} failed in GitHub Actions env: {e}") - - def test_dependabot_specific_scenarios(self): - """Test scenarios specific to Dependabot operations""" - if dependabot_test is None: - pytest.skip("dependabot_test module not available") - - dependabot_env = { - "GITHUB_ACTOR": "dependabot[bot]", - "GITHUB_EVENT_NAME": "pull_request", - "GITHUB_HEAD_REF": "dependabot/npm_and_yarn/package-1.0.0", - } - - with patch.dict(os.environ, dependabot_env): - functions = [ - getattr(dependabot_test, name) - for name in dir(dependabot_test) - if callable(getattr(dependabot_test, name)) and not name.startswith("_") - ] - - for func in functions: - try: - func() - except Exception as e: - print(f"Function {func.__name__} in Dependabot context: {e}") - - -if __name__ == "__main__": - pytest.main([__file__]) +import pytest +import yaml +from packaging.requirements import Requirement + +ROOT = Path(__file__).resolve().parents[1] + + +def read_yaml(path): + return yaml.safe_load((ROOT / path).read_text()) + + +def test_dependabot_configuration_is_valid_and_complete(): + config = read_yaml(".github/dependabot.yml") + assert config["version"] == 2 + updates = config["updates"] + assert len(updates) == 3 + assert {(item["package-ecosystem"], item["directory"]) for item in updates} == { + ("pip", "/"), + ("npm", "/frontend"), + ("docker", "/"), + } + assert all(item["schedule"]["interval"] == "weekly" for item in updates) + + +@pytest.mark.parametrize( + "ecosystem,manifest", + [("pip", "requirements.txt"), ("npm", "package.json"), ("docker", "Dockerfile")], +) +def test_each_dependabot_update_has_a_real_manifest(ecosystem, manifest): + update = next( + item + for item in read_yaml(".github/dependabot.yml")["updates"] + if item["package-ecosystem"] == ecosystem + ) + assert (ROOT / update["directory"].lstrip("/") / manifest).is_file() + + +@pytest.mark.parametrize( + "package", ["mcp", "numpy", "psycopg2-binary", "PyYAML", "packaging"] +) +def test_ci_installs_packages_imported_by_the_suite(package): + requirements = { + Requirement(line).name.lower() + for line in (ROOT / "requirements-ci.txt").read_text().splitlines() + if line.strip() and not line.lstrip().startswith("#") + } + assert package.lower() in requirements + + +def test_ci_propagates_test_failures(): + workflow = read_yaml(".github/workflows/python-ci.yml") + job = workflow["jobs"]["lint-test"] + assert job.get("continue-on-error", False) is False + assert job["strategy"]["matrix"]["python-version"] == ["3.10", "3.11"] + test_step = next( + step for step in job["steps"] if step.get("name") == "Run tests with coverage" + ) + assert test_step["run"].strip() == "pytest --cov=." + assert test_step.get("continue-on-error", False) is False diff --git a/tests/test_mcp_baseline_verifier.py b/tests/test_mcp_baseline_verifier.py new file mode 100644 index 0000000..54385cd --- /dev/null +++ b/tests/test_mcp_baseline_verifier.py @@ -0,0 +1,53 @@ +"""Exercise the verifier's success and failure paths without external services.""" + +import asyncio + +import pytest + +from mcp_server.main import MCPServer +from scripts.verify_mcp_baseline import verify_baseline + + +def test_verifier_checks_actual_handler(): + result = asyncio.run(verify_baseline()) + assert result["analysis"]["functions"] == 1 + + +@pytest.mark.parametrize("method", ["initialize", "tools/list", "tools/call"]) +def test_verifier_rejects_rpc_error_at_each_step(method): + class FailingHandler(MCPServer): + async def handle_request(self, request): + if request["method"] == method: + return { + "jsonrpc": "2.0", + "id": request["id"], + "error": {"code": -32000}, + } + return await super().handle_request(request) + + with pytest.raises(RuntimeError, match=method + " failed"): + asyncio.run(verify_baseline(FailingHandler())) + + +def test_verifier_rejects_wrong_analysis_even_when_rpc_succeeds(): + class IncorrectAnalysis(MCPServer): + async def handle_request(self, request): + response = await super().handle_request(request) + if request["method"] == "tools/call": + response["result"]["result"]["content"][0]["text"] = "{}" + return response + + with pytest.raises(RuntimeError, match="unexpected analysis"): + asyncio.run(verify_baseline(IncorrectAnalysis())) + + +def test_verifier_rejects_missing_advertised_tool(): + class MissingTool(MCPServer): + async def handle_request(self, request): + response = await super().handle_request(request) + if request["method"] == "initialize": + response["result"]["capabilities"]["tools"] = [] + return response + + with pytest.raises(RuntimeError, match="expected tools"): + asyncio.run(verify_baseline(MissingTool())) diff --git a/tests/test_mcp_compliance.py b/tests/test_mcp_compliance.py index d11ceda..5bb7c31 100644 --- a/tests/test_mcp_compliance.py +++ b/tests/test_mcp_compliance.py @@ -11,7 +11,7 @@ # Import MCP components from config.mcp_config import MCPConfig -from mcp_server.real_mcp_server import RealMCPServer +from mcp_server.real_mcp_server import mcp from connectors.dwave_quantum_connector import DWaveQuantumConnector from agents.a2a_mcp_integration import MCPEnabledA2AAgent, a2a_mcp_orchestrator @@ -20,15 +20,14 @@ class TestMCPCompliance: """Test suite to ensure MCP compliance across the codebase""" @pytest.fixture - async def mcp_config(self): + def mcp_config(self): """Get MCP configuration""" return MCPConfig() @pytest.fixture - async def mcp_server(self): - """Create real MCP server instance""" - server = RealMCPServer() - return server + def mcp_server(self): + """Use the actual registered FastMCP server.""" + return mcp def test_no_mock_endpoints(self, mcp_config): """Test that no mock endpoints are configured""" @@ -86,50 +85,79 @@ async def test_quantum_requires_real_qpu(self): assert "No D-Wave QPU available" in str(e) @pytest.mark.asyncio - async def test_data_processor_no_simulation(self): + async def test_data_processor_no_simulation(self, tmp_path): """Test that data processor doesn't return simulated results""" from protocols.data_processor import task as process_data - result = process_data() + (tmp_path / "records.json").write_text(json.dumps([{"value": 1}, {"value": 2}])) + result = process_data(str(tmp_path)) # Should either succeed with real data or fail if result["success"]: assert "simulation" not in str(result).lower() assert "simulated" not in str(result).lower() - assert result.get("directory") is not None + assert result["directory"] == str(tmp_path) + assert result["files_processed"] == 1 + assert result["total_records"] == 2 else: # Failed because no data directory - this is expected assert "error" in result assert "No data directory found" in result["error"] @pytest.mark.asyncio - async def test_mcp_server_tools_are_real(self, mcp_server): - """Test that MCP server tools perform real operations""" - # Test data processing tool - result = await mcp_server.server.tool("process_data")( - data_path="./test_data", operation="analyze" + async def test_mcp_server_tools_are_real(self, mcp_server, tmp_path): + """Dispatch the registered tool and verify actual filesystem analysis.""" + sample = tmp_path / "records.json" + sample.write_text(json.dumps([{"value": 1}])) + content = await mcp_server.call_tool( + "process_data", {"data_path": str(tmp_path), "operation": "analyze"} ) + result = json.loads(content[0].text) + assert result["status"] == "success" + assert result["file_count"] == 1 + assert result["total_size_bytes"] == sample.stat().st_size + assert result["file_types"] == {".json": 1} - # Should return real analysis or error, never mock data - assert "mock" not in str(result).lower() - assert "simulated" not in str(result).lower() + @pytest.mark.asyncio + async def test_mcp_server_reports_missing_data(self, mcp_server, tmp_path): + content = await mcp_server.call_tool( + "process_data", + {"data_path": str(tmp_path / "missing"), "operation": "analyze"}, + ) + result = json.loads(content[0].text) + assert result["status"] == "error" + assert "does not exist" in result["error"] @pytest.mark.asyncio - async def test_agents_use_real_mcp(self): - """Test that agents use real MCP for communication""" + async def test_agents_use_real_mcp(self, mcp_server, tmp_path, monkeypatch): + """Exercise the agent against registered tools without a network provider.""" + sample = tmp_path / "sample.txt" + sample.write_text("real fixture data") agent = MCPEnabledA2AAgent("test_agent", ["analyze"]) + calls = [] + + async def dispatch_registered_tool(name, arguments): + calls.append((name, arguments)) + content = await mcp_server.call_tool(name, arguments) + return json.loads(content[0].text) + + monkeypatch.setattr(agent, "_execute_mcp_tool", dispatch_registered_tool) + result = await agent._analyze_data({"path": str(tmp_path)}) + assert calls[0] == ( + "process_data", + {"data_path": str(tmp_path), "operation": "analyze"}, + ) + assert result["analysis_type"] == "comprehensive" + assert result["status"] == "success" + assert result["file_count"] == 1 + assert result["total_size"] == sample.stat().st_size + assert result["confidence"] > 0 - # Test data analysis uses real MCP - result = await agent._analyze_data({"path": "./data"}) - - # Should return real analysis results - assert result["analysis_type"] in ["comprehensive", "failed"] - assert "confidence" in result - - # Test code generation result = await agent._generate_code({"type": "function", "language": "python"}) - - assert result["code_type"] in ["function", "error"] + assert calls[1][0] == "execute_code" + assert calls[1][1]["language"] == "python" + assert "def process" in calls[1][1]["code"] + assert result["code_type"] == "function" assert "code" in result def test_no_placeholder_code_in_production(self): @@ -226,13 +254,19 @@ def test_integration_points_documented(self): for doc in required_docs: doc_path = Path(doc) - if doc_path.exists(): - content = doc_path.read_text() - - # Check for required sections - assert "Real" in content or "real" in content - assert "mock" not in content.lower() or "no mock" in content.lower() - assert "MCP" in content + assert doc_path.is_file(), f"Missing integration document: {doc}" + assert "MCP" in doc_path.read_text() + + # Migration plans legitimately discuss mocks. Verify the current runtime + # map against files that exist instead of banning a word in historical plans. + repo_map = Path("docs/REPO_MAP.md").read_text() + for source in [ + "mcp_server/main.py", + "mcp_server/real_mcp_server.py", + "agents/a2a_mcp_integration.py", + ]: + assert Path(source).is_file() + assert source in repo_map, f"Undocumented integration: {source}" def test_env_template_exists(self): """Test that .env template exists with all required variables""" From 18057fe078d060b43f758a1031156c18d1f25f9b Mon Sep 17 00:00:00 2001 From: Hayden <154503486+groupthinking@users.noreply.github.com> Date: Thu, 29 Oct 2026 05:52:37 -0500 Subject: [PATCH 5/5] fix: resolve project imports in CI pytest invocation --- pytest.ini | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/pytest.ini b/pytest.ini index 76779c6..da320a2 100644 --- a/pytest.ini +++ b/pytest.ini @@ -1,4 +1,5 @@ [pytest] +pythonpath = . testpaths = tests python_files = test_*.py python_classes = Test* @@ -7,4 +8,4 @@ addopts = -v --tb=short markers = slow: marks tests as slow (deselect with '-m "not slow"') integration: marks tests as integration tests - unit: marks tests as unit tests \ No newline at end of file + unit: marks tests as unit tests