Skip to content

feat(kafka): multi-cluster support (named clusters + --cluster + v3 migrate)#45

Merged
HumanBean17 merged 12 commits into
mainfrom
feat/kafka-multi-cluster
Jul 10, 2026
Merged

feat(kafka): multi-cluster support (named clusters + --cluster + v3 migrate)#45
HumanBean17 merged 12 commits into
mainfrom
feat/kafka-multi-cluster

Conversation

@HumanBean17

Copy link
Copy Markdown
Owner

Summary

Adds support for multiple named Kafka clusters in one agctl.yaml, mirroring the existing database.connections model. Previously Config.kafka was a single object (one broker set, one TLS block) — asymmetric with database.connections / services / logs.sources, which are all named maps.

What changed

  • Config schema v3kafka: restructured from a single object to kafka.clusters.<name> (named map, each with brokers/ssl/timeout_seconds/default_consumer_group/schema_registry_url) + kafka.default_cluster. KafkaCluster model; KafkaSSL reused unchanged.
  • Cluster resolution — new resolve_cluster_name mirrors resolve_connection_name (db_commands.py): precedence --cluster > pattern/reactor .cluster > kafka.default_cluster > single-cluster auto-default. new_kafka_client(cluster, …) now takes a resolved KafkaCluster.
  • CLI--cluster <name> on kafka produce/consume/assert; kafka assert --pattern resolves both topic and cluster from the pattern.
  • Mock reactorsMockEngine takes a per-reactor client map (kafka_clients: dict[reactor_name→client]); each mocks.kafka.reactors.<name> can target its own cluster via an optional cluster field.
  • config migrate — lifts a flat kafka: block into kafka.clusters.default, handles v1→v3 (jq + structural, one pass) and v2→v3 (structural only — v2 exprs are already envelope-rooted, so no double-prefix). Bumps version to "3". Envelope key already_v2already_current.
  • Discoverkafka-patterns item detail surfaces the resolved cluster.
  • Docs — DESIGN §2.1/§3.2/§3.7/§5, ARCHITECTURE §5/§8/§15, and consumer skills/ synced to v3.

⚠️ Breaking change (config version 2 → 3)

A version: "2" (or "1") config is now rejected at load with a pointer to migrate. Run:

agctl config migrate        # backs up to <path>.bak, lifts kafka → clusters, bumps to v3

The migration is non-mutating on input, idempotent on v3, and safe for v1 and v2 sources.

Testing

  • 962 unit tests passing (was ~947 pre-feature). New coverage: cluster resolution precedence, unknown/no-cluster errors, migrate_config (v1/v2→v3 one-pass, idempotency, non-mutation), validator per-cluster cross-refs, per-cluster mock-reactor wiring + shared-cluster client reuse, discover cluster surfacing, and the v1/v2 cli_flags_note gate.
  • Integration: existing require_kafka suite runs against the default cluster unchanged; a new self-skipping require_kafka_second_broker round-trip is gated on AGCTL_TEST_KAFKA_BROKER_2.
  • Unit tests run without confluent_kafka installed (fakes), per convention.

Review

Per-task reviews (sonnet) all Approved; final whole-branch review (opus) verdict Ready to merge — with fixes. The single Important finding (config migrate's cli_flags_note unconditionally told v2→v3 migrators to prefix .body |/.value | on CLI flags, which would double-prefix working scripts) was fixed by gating that advice on v1 sources only (commit 55e606c). No Critical issues.

Spec / Plan

  • Spec: docs/superpowers/specs/active/2026-07-09-agctl-kafka-multi-cluster-design.md
  • Plan: docs/superpowers/plans/active/2026-07-09-agctl-kafka-multi-cluster.md

(These stay in active/ until the PR merges, then move to archive/ as ADRs.)


🤖 Generated with Claude Code

HumanBean17 and others added 12 commits July 9, 2026 23:49
Brainstormed design (named kafka.clusters mirroring database.connections,
--cluster flag, per-pattern/reactor cluster binding, v2->v3 + config migrate)
and the 4-task implementation plan.

Co-Authored-By: Claude <noreply@anthropic.com>
Co-Authored-By: Claude <noreply@anthropic.com>
Co-Authored-By: Claude <noreply@anthropic.com>
Co-Authored-By: Claude <noreply@anthropic.com>
Co-Authored-By: Claude <noreply@anthropic.com>
Co-Authored-By: Claude <noreply@anthropic.com>
Co-Authored-By: Claude <noreply@anthropic.com>
Co-Authored-By: Claude <noreply@anthropic.com>
…de review)

Batched application of 8 fixes from the 4-reviewer fan-out for the
Multi-Cluster Kafka feature (PR #45).

1. discover no longer hard-fails on an unresolvable cluster: the
   kafka-patterns item-detail resolve_cluster_name call is wrapped so a
   ConfigError yields cluster=null (the item still renders topic/match/
   params/example). A cluster-agnostic pattern with >1 cluster and no
   default_cluster passes validation, so an inspection command must not
   exit 2 on it.
2. resolve_cluster_name is now keyword-only (*, explicit=None,
   binding_cluster=None), mirroring the DB analog resolve_connection_name;
   all call sites updated to keyword form.
3. --cluster help shortened to "Cluster name override" across
   produce/consume/assert (the old text over-promised a single default
   source).
4. mock_run re-raises resolve_cluster_name failures with reactor context,
   so a dangling reactor.cluster / unresolvable cluster names the reactor.
5. migrate: comment at the v1 gate now states the missing/legacy-version
   assumption explicitly (do not broaden the gate).
6. migrate: _migrate_* helper rewrites param widened to
   list[dict[str, Any]] (structural rewrites carry non-str before/after).
7. Renamed test_mock_kafka_requires_resolvable_default_cluster_error to
   test_mock_kafka_reactor_no_clusters_error (accurate: builds no clusters).
8. Added test_kafka_consume_explicit_cluster (mirrors the produce test).

Tests: covering (discover/kafka/mock/migrate/validator) 160 passed;
full unit suite 964 passed (+2).

Co-Authored-By: Claude <noreply@anthropic.com>
Following the discover cluster-resilience fix (f6901d0), the kafka-patterns
discover.item now yields cluster: null (instead of exit 2) for a
cluster-agnostic pattern with >1 cluster and no default_cluster. The
discover.item shape contract now reflects this nullable case.

Co-Authored-By: Claude <noreply@anthropic.com>
The v2->v3 version bump (Task 1) migrated unit fixtures but missed the
inline integration configs in test_logs_commands.py, test_mock_daemon.py,
and test_mock_commands.py (_build_config + reactor e2e). They still had
version: "2" / flat kafka, so the v3 guard rejected them at load ->
"Config dialect v2 is no longer supported". CI (full suite) caught what
unit-only review runs missed. Bump to v3 + convert flat kafka to clusters.

Co-Authored-By: Claude <noreply@anthropic.com>
@HumanBean17
HumanBean17 merged commit 99060a1 into main Jul 10, 2026
3 checks passed
@HumanBean17
HumanBean17 deleted the feat/kafka-multi-cluster branch July 10, 2026 07:53
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