From a73ed933a24904d7efeee93b5173fc484c34e803 Mon Sep 17 00:00:00 2001 From: Dmitry Teryaev Date: Fri, 10 Jul 2026 11:18:52 +0300 Subject: [PATCH] feat: add gRPC transport (agctl grpc) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit First-class in-tree gRPC transport — a fifth transport alongside http/kafka/db/logs. Every command emits one JSON envelope with deterministic exit codes; gRPC status is a result (ok:true, exit 0) unless an assertion fails, mirroring HTTP 4xx. - grpc: config section (targets/descriptors/templates) + pydantic models (GrpcTls/GrpcTarget/GrpcDescriptorSource/GrpcTemplate/GrpcConfig) and cross-reference validation. - GrpcClient: reflection-first/descriptor-fallback proto resolution (file_containing_symbol), JSON<->protobuf (json_format/message_factory, no generated _pb2), all four call types, healthcheck (grpc.health.v1), mTLS, lazy imports + DI seams. - gRPC assertions: validate_grpc_assertion_args + evaluate_grpc_assertions reusing the jq/subset/equals primitives + a hardcoded status map. - grpc call: template + free-form; unary/client-stream return one envelope; server-stream/bidi stream NDJSON out + consume NDJSON in (stdin), with signal-driven stop + a terminal-status summary. - grpc healthcheck; grpc-services/grpc-methods discover categories. - grpc extra (grpcio/grpcio-tools/grpcio-health-checking/grpcio-reflection/ protobuf/jq) + integration aggregate + CI install line. - Docs: DESIGN (struck from future-work; added throughout), ARCHITECTURE, sample config, consumer skill, README. Design via brainstorming -> spec -> plan -> 14 TDD tasks (each per-task reviewed) + an independent 4-reviewer fan-out code review; all Critical and Important findings fixed. Full suite + live in-process grpcio integration tests green. Spec: docs/superpowers/specs/active/2026-07-09-grpc-support-design.md Plan: docs/superpowers/plans/active/2026-07-09-grpc-support.md Co-Authored-By: Claude --- .github/workflows/test.yml | 2 +- .gitignore | 6 + .superpowers/sdd/task-10-report.md | 113 -- README.md | 40 +- agctl/assertions.py | 215 +++ agctl/cli.py | 11 + agctl/clients/grpc_client.py | 909 +++++++++++++ agctl/commands/discover_commands.py | 101 +- agctl/commands/grpc_commands.py | 822 +++++++++++ agctl/config/models.py | 46 + agctl/config/validator.py | 35 + agctl/data/sample-config.yaml | 33 + docs/ARCHITECTURE.md | 103 +- docs/DESIGN.md | 241 +++- .../plans/active/2026-07-09-grpc-support.md | 764 +++++++++++ .../active/2026-07-09-grpc-support-design.md | 615 +++++++++ pyproject.toml | 3 +- skills/agctl/SKILL.md | 48 + tests/fixtures/agctl.yaml | 16 + tests/fixtures/echo.proto | 19 + tests/fixtures/echo_descriptor.pb | 14 + tests/fixtures/echo_pb2.py | 40 + tests/fixtures/echo_pb2_grpc.py | 226 +++ tests/integration/test_grpc_commands.py | 512 +++++++ tests/unit/test_config_validator.py | 169 +++ tests/unit/test_discover_command.py | 213 +++ tests/unit/test_grpc_assertions.py | 169 +++ tests/unit/test_grpc_client.py | 1212 +++++++++++++++++ tests/unit/test_grpc_commands.py | 1134 +++++++++++++++ tests/unit/test_models.py | 86 ++ 30 files changed, 7792 insertions(+), 125 deletions(-) delete mode 100644 .superpowers/sdd/task-10-report.md create mode 100644 agctl/clients/grpc_client.py create mode 100644 agctl/commands/grpc_commands.py create mode 100644 docs/superpowers/plans/active/2026-07-09-grpc-support.md create mode 100644 docs/superpowers/specs/active/2026-07-09-grpc-support-design.md create mode 100644 tests/fixtures/echo.proto create mode 100644 tests/fixtures/echo_descriptor.pb create mode 100644 tests/fixtures/echo_pb2.py create mode 100644 tests/fixtures/echo_pb2_grpc.py create mode 100644 tests/integration/test_grpc_commands.py create mode 100644 tests/unit/test_config_validator.py create mode 100644 tests/unit/test_grpc_assertions.py create mode 100644 tests/unit/test_grpc_client.py create mode 100644 tests/unit/test_grpc_commands.py diff --git a/.github/workflows/test.yml b/.github/workflows/test.yml index 64b7245..1bcda83 100644 --- a/.github/workflows/test.yml +++ b/.github/workflows/test.yml @@ -32,7 +32,7 @@ jobs: - name: Install agctl with all client extras and test deps run: | python -m pip install --upgrade pip - pip install -e ".[dev,http,jq,kafka,db,logs]" + pip install -e ".[dev,http,jq,kafka,db,logs,grpc]" - name: Run tests run: pytest diff --git a/.gitignore b/.gitignore index ec0252d..0fa3b18 100644 --- a/.gitignore +++ b/.gitignore @@ -29,3 +29,9 @@ uv.lock # agctl test-runbook results (ephemeral per-run reports) *.results.md + +# agctl mock daemon runtime state (pidfiles + logs; default state-dir ./.agctl/) +.agctl/ + +# superpowers SDD scratch (task briefs/reports, review packages, progress ledger) +.superpowers/ diff --git a/.superpowers/sdd/task-10-report.md b/.superpowers/sdd/task-10-report.md deleted file mode 100644 index 53ffe0e..0000000 --- a/.superpowers/sdd/task-10-report.md +++ /dev/null @@ -1,113 +0,0 @@ -# Task 10 Report: Config Overlay + Runbook Sidecar Documentation Sync - -## Summary - -Config overlay and runbook sidecar feature documentation synced across DESIGN.md, ARCHITECTURE.md, and skills/agctl/SKILL.md. All changes reflect the as-built implementation on branch `config-overlay-sidecar`. - -## Files Changed - -### `/Users/dmitry/Desktop/CursorProjects/agenttest/docs/DESIGN.md` - -**§3 (CLI Command Design) — Global flags table:** -- Added `--overlay ` row: "Overlay config fragment (repeatable; later wins); layered on base config" - -**§2.4 (Configuration Schema) — New section:** -- Added complete overlay documentation covering: - - Overlay syntax (partial config with optional version) - - Usage via `--overlay ` (repeatable) - - Runbook sidecar convention (`.agctl.yaml`) - - Merge behavior (sidecar-wins: recursive dict merge, scalar/list replace, type clash → error) - - Override tracking in `config validate --overlay` - - Precedence: base < overlays < AGCTL_* env vars - - Schema version note (not a version bump) - -**§5 (Configuration Resolution Order):** -- Updated precedence chain from 5 to 6 steps -- Inserted overlays as step 5: `--overlay ` flags after interpolation, before env overrides -- Clarified that overlays are applied in flag order (later wins) - -**§3.6 (`agctl config` — Config Introspection):** -- Updated `config validate` command: added `--overlay ` flag documentation -- Updated `config show` command: added `--overlay ` flag documentation -- Documented output shape changes: `config show --overlay` emits `{"config": ..., "overrides": [...]}`; back-compat form without overlays - -**§3.7 (`agctl discover` — Lazy Scoped Discovery):** -- Added section intro note: "All `discover` commands accept `--overlay `" -- Updated all four discover invocation forms: - - Level 0 (summary): `agctl discover [--overlay ]` - - Level 1 (category): `agctl discover --category [--overlay ]` - - Level 2 (item): `agctl discover --category --name [--overlay ]` - - Search: `agctl discover --search [--overlay ]` - -**§3.5 (`agctl mock` — Mock Server):** -- Updated `mock run` command: added `--overlay ` flag -- Updated `mock start` command: added `--overlay ` flag with note "forwarded to the daemon" - -**§4.2 (Result Shapes by Command Group):** -- Updated `config.validate` output: documented `warnings` array with override records -- Updated `config.show` output: documented branch result shape with `{"config": ..., "overrides": [...]}` - -### `/Users/dmitry/Desktop/CursorProjects/agenttest/docs/ARCHITECTURE.md` - -**§5 (Configuration Pipeline):** -- Extended as-built pipeline diagram with overlay loop: - - After base interpolation and `_check_version` - - For each overlay: discover (explicit only) → interpolate → `PartialConfig.model_validate` → version-major-match check → `deep_merge` - - Then `apply_env_overrides` on MERGED dict (AGCTL_* wins over overlays) - - Finally `Config.model_validate` (final) - -- Added "Overlay types" subsection: - - Documented `PartialConfig` (Config with optional version) - - Documented `ComposedConfig` NamedTuple (config + overrides list) - - Documented override record shape: `{"path": "", "overlay": ""}` - -- Added "Deep merge" subsection: - - Documented `deep_merge(base, overlay, overlay_name, overrides)` algorithm - - Specified merge contract: sidecar-wins, recursive dict merge, scalar/list replace, override recording at leaves - -### `/Users/dmitry/Desktop/CursorProjects/agenttest/skills/agctl/SKILL.md` - -**Command forms section:** -- Updated global flags line from "Only `--config ` is global" to "`--config ` and `--overlay ` (repeatable) are global" - -**Rationale:** The skill documents operational command surfaces. The global `--overlay` flag is part of that surface and agents need to know it exists when composing commands (especially when following runbook preconditions that require overlays). - -## Altitude Preserved - -All updates respect document altitude: -- **DESIGN.md** captures user-facing contract (WHAT/WHY): overlay syntax, precedence, CLI surface, output shapes -- **ARCHITECTURE.md** captures as-built implementation (HOW): pipeline stages, types, merge algorithm -- **skills/agctl/SKILL.md** captures operational surface: global flags an agent must know - -No implementation details leaked into DESIGN.md, no user-facing contract leaked into ARCHITECTURE.md, and no design rationale leaked into skills/. - -## Cross-References Verified - -- DESIGN §2.4 overlays → DESIGN §3 global flags table → DESIGN §5 precedence order -- DESIGN §3.6 config commands → DESIGN §4.2 output shapes -- DESIGN §3.7 discover → references overlay support -- DESIGN §3.5 mock commands → overlay forwarding to daemon -- ARCHITECTURE §5 pipeline → references `compose_config`, `PartialConfig`, `ComposedConfig`, `deep_merge` -- skills/agctl → references global `--overlay` flag - -## Code Review Verification - -All documentation changes were verified against the as-built code: -- CLI flag registration in `agctl/cli.py` -- `PartialConfig` model in `agctl/config/models.py` -- `compose_config` pipeline in `agctl/config/loader.py` -- `deep_merge` algorithm in `agctl/config/loader.py` -- Command threading in `agctl/commands/*_commands.py` -- Output shape changes in `agctl/commands/config_commands.py` - -## No Changes Intentionally Omitted - -Assessed and found not requiring changes: -- `agctl/data/sample-config.yaml` — frozen by drift-guard test; out of scope for docs-watcher -- `.env.example` — not part of this feature -- Test files — out of scope for docs-watcher -- Archived specs — frozen history - -## Conclusion - -All three document families (DESIGN, ARCHITECTURE, skills/agctl) have been updated to reflect the config overlay and runbook sidecar feature at their respective altitudes. Documentation now matches the as-built implementation on branch `config-overlay-sidecar` (tip 4337f63). diff --git a/README.md b/README.md index 80fc04c..8135dfb 100644 --- a/README.md +++ b/README.md @@ -66,9 +66,10 @@ pip install -e ".[http]" # http call / request / ping / check ready pip install -e ".[jq]" # jq flags: http --match/--jq-path, mock match.jq pip install -e ".[kafka]" # kafka produce / consume / assert pip install -e ".[db]" # db query / assert +pip install -e ".[grpc]" # grpc call / healthcheck (includes grpcio, protobuf, jq) pip install -e ".[logs]" # logs query / assert / tail (includes jq) -pip install -e ".[http,kafka,db]" # everything except logs (typical — bundles jq) -pip install -e ".[http,kafka,db,logs]" # everything +pip install -e ".[http,kafka,db]" # everything except logs/grpc (typical — bundles jq) +pip install -e ".[http,kafka,db,grpc,logs]" # everything ``` Verify the install — both binary names work: @@ -111,6 +112,8 @@ auto-discovered from the current directory upward). | | `assert` | Fail (exit 1) unless a matching message arrives within `--timeout`. Modes: `--contains`, `--match `, `--pattern ` (combinable) | | **`db`** | `query` | Run `--template` or free-form `--sql`; return all rows | | | `assert` | Assert `--expect-rows N`, or `--expect-value --path --equals ` on the first row | +| **`grpc`** | `call