Skip to content

feat: add gRPC transport (agctl grpc)#46

Merged
HumanBean17 merged 1 commit into
mainfrom
feat/grpc-support
Jul 10, 2026
Merged

feat: add gRPC transport (agctl grpc)#46
HumanBean17 merged 1 commit into
mainfrom
feat/grpc-support

Conversation

@HumanBean17

Copy link
Copy Markdown
Owner

Summary

Adds gRPC as a first-class, in-tree transport (agctl grpc) — a fifth transport alongside http/kafka/db/logs. Every command emits one JSON envelope with deterministic exit codes (0/1/2); gRPC status is a result (ok:true, exit 0) unless an assertion fails, mirroring HTTP 4xx.

What's added

  • grpc: config sectiontargets (named endpoints, TLS/plaintext, reflection mode), descriptors (reflection fallback: .proto globs or pre-compiled descriptor_set), templates (named calls). New pydantic models GrpcTls/GrpcTarget/GrpcDescriptorSource/GrpcTemplate/GrpcConfig + cross-reference validation.
  • GrpcClient (agctl/clients/grpc_client.py) — reflection-first/descriptor-fallback proto resolution (via file_containing_symbol), JSON↔protobuf serialization (json_format + message_factory, no generated _pb2), all four call types (unary / server-stream / client-stream / bidi), healthcheck (grpc.health.v1). Lazy grpcio/protobuf imports + DI seams.
  • gRPC assertions (agctl/assertions.py) — new validate_grpc_assertion_args / evaluate_grpc_assertions reusing the jq/subset/equals primitives; gRPC status name↔code map (hardcoded, keeps the module grpc-free).
  • grpc call (agctl/commands/grpc_commands.py) — template + free-form modes; unary/client-streaming return one envelope; server-streaming/bidi stream NDJSON to stdout and consume NDJSON from stdin (the bidirectional streaming symmetry), with signal-driven stop + a terminal-status summary. grpc healthcheck (the gRPC analog of check ready).
  • Discoverygrpc-services and grpc-methods categories (target listing + method request-field schema).
  • Packaging — new grpc extra (grpcio, grpcio-tools, grpcio-health-checking, grpcio-reflection, protobuf, jq); added to the integration aggregate and the CI install line. In-tree — no new entry-point group.
  • Docs — DESIGN.md (struck from §10 future-work; added throughout), ARCHITECTURE.md, sample config, consumer skill, README.

Design decisions (spec D1–D13)

In-tree (not a plugin); reflection-first with descriptor fallback; JSON in/out for messages; all four call types via stdin/stdout NDJSON symmetry; package.Service/Method addressing + --address no-config mode; status-as-result; unary assertions reuse jq/subset/equals; gRPC metadata + trailers; TLS/plaintext mirroring kafka.ssl; descriptor-driven dynamic stubs (no codegen).

Testing

  • 1023 passed, 17 skipped (full suite). Unit tests are dependency-free (fake-client injection + pytest.importorskip), so the suite collects and runs without the grpc extra.
  • Integration tests (tests/integration/test_grpc_commands.py): an in-process grpcio Echo server (all four call types + reflection + health), self-skipping without AGCTL_TEST_LIVE=1 / AGCTL_TEST_GRPC_ADDR.
  • Design/implementation via brainstorming → spec → plan → 14 TDD tasks, each per-task reviewed; a final whole-branch review (cross-cutting) found and fixed two Important issues (mTLS tls fields now honored; streaming mid-run errors emit a summary line, never a raw traceback).

Known limitations / deferred (post-merge)

Tracked in .superpowers/sdd/progress.md: descriptor-pool is resolved per call (no caching — reflection/protoc runs once per invocation); a now-unreachable streaming NotImplementedError stub in _grpc_call_core; a latent self._grpc attribute path on the channel-injected DI seam (not hit in production); --match root semantics (envelope on unary, message on streams) noted in docstrings but not the CLI help; minor DRY/coverage nits. Cross-message ordering assertions and a gRPC mock server are out of scope (future work).

References

  • Spec: docs/superpowers/specs/active/2026-07-09-grpc-support-design.md
  • Plan: docs/superpowers/plans/active/2026-07-09-grpc-support.md

After merge: move the spec and plan from specs/active/ + plans/active/ to archive/ (ADR transition).

🤖 Generated with Claude Code

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 <noreply@anthropic.com>
@HumanBean17
HumanBean17 merged commit 600dd4e into main Jul 10, 2026
3 checks passed
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant