Skip to content

fix: KRaft controller pods are now dynamically added/removed from the voter list - #1007

Closed
razvan wants to merge 30 commits into
mainfrom
impl/kraft-dynamic-voter-membership
Closed

fix: KRaft controller pods are now dynamically added/removed from the voter list#1007
razvan wants to merge 30 commits into
mainfrom
impl/kraft-dynamic-voter-membership

Conversation

@razvan

@razvan razvan commented Aug 14, 2026

Copy link
Copy Markdown
Member

Description

Do not review yet. This is very unpolished and unverified.

Definition of Done Checklist

  • Not all of these items are applicable to all PRs, the author should update this template to only leave the boxes in that are relevant
  • Please make sure all these things are done and tick the boxes

Author

  • Changes are OpenShift compatible
  • CRD changes approved
  • CRD documentation for all fields, following the style guide.
  • Helm chart can be installed and deployed operator works
  • Integration tests passed (for non trivial changes)
  • Changes need to be "offline" compatible
  • Links to generated (nightly) docs added
  • Release note snippet added

Reviewer

  • Code contains useful comments
  • Code contains useful logging statements
  • (Integration-)Test cases added
  • Documentation added or updated. Follows the style guide.
  • Changelog updated
  • Cargo.toml only contains references to git tags (not specific commits or branches)

Acceptance

  • Feature Tracker has been updated
  • Proper release label has been added
  • Links to generated (nightly) docs added
  • Release note snippet added
  • Add type/deprecation label & add to the deprecation schedule
  • Add type/experimental label & add to the experimental features tracker

razvan and others added 15 commits August 14, 2026 10:31
Covers admitting/removing controllers from the KRaft dynamic quorum's
voter set on scale-up/scale-down, via a self-managed sidecar container
rather than operator-side kube-exec.

Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
- clarify kraft_mode_cluster() fixture is owned by Task 5, not Task 3/4
- add missing test coverage for the Kerberos-disables-sidecar global constraint

Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
Compute a comma-joined host:port list of KRaft controller voters in
build_controller_rolegroup_statefulset, reusing the existing
pod_descriptors(...) call instead of calling it twice. Stored as a
local variable (currently _-prefixed, unused) for Task 4 to consume
when building the controller sidecar container's env vars.

Test-feasibility note: the brief assumed KafkaPodDescriptor's
pub(crate) fields require the test to live inside crd/mod.rs. That's
incorrect: pub(crate) is crate-wide visibility, not module-scoped, so
KafkaPodDescriptor can be constructed directly from any module in
this crate. Added a real unit test for kraft_controllers (the
existing pure join helper, previously untested) in
controller/build/properties/mod.rs, constructing KafkaPodDescriptor
values via NamespaceName/StatefulSetName/ServiceName's FromStr and
DomainName::try_from, and asserting the comma-joined host:port output
while confirming non-controller roles are filtered out.

Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
Address review findings on the quorum-manager sidecar (Task 4):

Critical:
- Mount the internal TLS volume (STACKABLE_TLS_KAFKA_INTERNAL_VOLUME_NAME)
  on the sidecar in addition to the config volume. controller_admin_client_
  properties always sets security.protocol=SSL and points its keystore/
  truststore at that directory, so without this mount every add-controller/
  remove-controller call failed SSL init and the feature was a no-op.
- Wrap every kafka-metadata-quorum.sh invocation in the preStop script with
  timeout 15, so a hung admin-client call can no longer burn into
  terminationGracePeriodSeconds regardless of the script's own 25s budget.
- Fix the preStop majority guard to only count describe --replication rows
  whose Status is a recognized voter value (Leader/Follower), excluding
  Observer rows, instead of treating every non-header row as a voter. If no
  recognized voter rows are found (e.g. a real column-layout mismatch), the
  check now fails closed (retries, never removes) instead of failing open.

Important:
- Log an explicit diagnostic when the main-loop's metrics scrape yields an
  empty/unrecognized Raft state, instead of looping silently forever.
- Fix the preStop loop so the "would break quorum majority" branch actually
  retries within the 25s deadline instead of breaking out after one attempt.
- Add a unit test asserting the sidecar mounts every directory referenced by
  admin-client.properties (config + internal TLS), and a unit test guarding
  that the add_common_kafka_env signature refactor (passing a pre-computed
  node_id_offset instead of computing it internally) left the broker's own
  NODE_ID_OFFSET env var value unchanged.

Minor:
- Removed the now-stale allow(dead_code) on supports_dynamic_quorum.
- Fixed the quorum_manager_pre_stop_command doc comment, which documented a
  non-existent node_id parameter.
- Moved the "must be confirmed against a live cluster" caveat about
  describe --replication's column layout out of the shipped bash script
  and into a Rust doc comment; dropped the internal-planning-doc reference.
- Named the sidecar's container name via a const instead of an allocated
  String.
- Guard against an empty POD_INDEX in the preStop script before the
  arithmetic that derives REPLICA_ID.

Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
Serializes scale-down so each controller's preStop hook (self-removal
from the KRaft voter set) completes before the next pod terminates.

Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
…uster sidecar bugs found while verifying

- tests/templates/kuttl/operations-kraft/{60,70}-assert.yaml.j2: insert the
  missing '---' document separator between the broker and controller
  StatefulSet assertions (the second document was silently dropped), and add
  a voter-count assertion using kafka-metadata-quorum.sh describe
  --replication, filtered to Leader/Follower rows (Observer rows include
  broker nodes, which also replicate the metadata log but aren't voters).

- rust/operator-binary/src/controller/build/command.rs: two real bugs found
  and fixed by running the operator against a live minikube cluster
  (closing out Task 4's deferred describe --replication verification):

  1. quorum_manager_container_command ran as the container's PID 1 with no
     signal trap, so it never noticed SIGTERM (the kernel suppresses the
     default action of unhandled signals for PID 1). Confirmed live: the
     kafka container in the same pod shut down promptly while this sidecar
     kept looping every ~15s until Kubernetes gave up and force-killed it
     after the full 1800s terminationGracePeriodSeconds, holding the whole
     pod (and the controller StatefulSet's scale-down) well past kuttl's
     step timeout. Fixed with a TERM trap plus an interruptible
     'sleep 10 &' / 'wait ' pair.

  2. add-controller was always failing with 'node.id not found in
     configuration file' because it was pointed at the plain
     admin-client.properties, which has no node.id. add-controller reads
     node.id/listeners from the same --command-config file it connects
     with, to build the voter registration payload — but the rendered
     controller.properties has no bare security.protocol/ssl.* (only
     listener.name.<name>.ssl.*-prefixed ones), so using it alone would
     leave the AdminClient unable to reach the TLS-only bootstrap
     controller. Fixed by rendering controller.properties (same
     REPLICA_ID derivation as the kafka container's own entrypoint) and
     concatenating it with admin-client.properties into a merged config
     used only for add-controller. Confirmed live: 'Added controller
     <id> with directory id ... and endpoints: ...' after the fix, versus
     'Timed out waiting for a node assignment' / 'node.id not found'
     before it.

Both fixes are backed by real describe --replication output and pod/sidecar
logs observed on a live minikube cluster; see the task report for the full
transcript. A full kuttl run of operations-kraft_kafka-kraft-4.2.1 was
in progress validating steps 20/25/30/50/60 (60's new voter-count assertion
included) when the run was stopped before reaching a final PASS/FAIL for the
whole suite — see report for exact status.

Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
@razvan razvan changed the title Impl/kraft dynamic voter membership fix: KRaft controller pods are now dynamically added/removed from the voter list Aug 14, 2026
razvan and others added 4 commits August 14, 2026 17:05
… assertions

Fix round for review findings on commit 58d2743 (Task 7). All 9 findings
addressed:

Critical:
- The quorum-manager sidecar was only given POD_NAME/NODE_ID_OFFSET, but its
  own controller.properties render needs ROLEGROUP_HEADLESS_SERVICE_NAME,
  NAMESPACE, CLUSTER_DOMAIN and KAFKA_CLIENT_PORT too, so its `listeners`
  value was most likely broken. Extracted controller_pod_shared_env_vars()
  (statefulset.rs) shared by the kafka container and the sidecar (including
  envOverrides), so they can't drift apart again. Added a regression test
  that renders the real controller.properties and asserts every
  ${env:...} placeholder it references has a matching env var on the
  sidecar.
- The sidecar's render/merge preamble (cp/config-utils template/cat) had no
  error handling, so a failure would silently start the retry loop with a
  stale config. Scoped 'set -e' to just the preamble (with 'set +e' after)
  so it crash-loops loudly instead. Also added the same
  '[ -n "$POD_INDEX" ] || exit 0' guard the preStop script already had to
  the main loop's REPLICA_ID derivation.

Important:
- Reversed the add-controller config concatenation order (controller.properties
  first, admin-client.properties last) so the client TLS config always wins
  on a key collision by construction, not by accident of there being no
  overlap today. Updated the doc comment to explain why the order matters.
- Added --max-time 5 --connect-timeout 2 to the metrics curl call in the main
  loop, so an unresponsive (not refused) connection can't block the
  SIGTERM-trap fix's prompt shutdown indefinitely.

Minor:
- Extracted DERIVE_POD_INDEX/EXPORT_REPLICA_ID shared constants for the
  REPLICA_ID derivation duplicated 4x across command.rs.
- Renamed two tests that only check literal command-string contents but were
  named as if they verified runtime behavior.
- Added comments noting the kuttl assert 'timeout: 30' field is known-inert
  for TestAssert commands, and that the hardcoded :9093 port couples to the
  test fixture's default TLS config.
- Fixed a doc-comment typo ('kept report as' -> 'kept reporting as').

Verified with cargo build/test/clippy/fmt --check (no live kuttl run, per
instruction - a live run already validated the underlying bugs this fix
round hardens against).

Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
…r membership

Fixes 5 issues found in the final review of the 9-task KRaft dynamic
voter membership plan:

1. Drop Kafka 3.7.x support entirely (scope change authorized by human
   partner): delete supports_dynamic_quorum and its tests, remove the
   3.7.x match/branch from initial_controllers_command (dropping the
   now-unused product_version parameter, which cascaded through
   broker_start_command/broker_kafka_container_commands/
   controller_kafka_container_command since it was threaded through
   solely for this), ungate build_quorum_manager_container on Kerberos
   alone, delete the obsolete 3.7 sidecar test, and update
   kraft-controller.adoc with a clear 3.9.0+ minimum-version statement
   and undefined-behavior warning for unsupported versions.

2. Fix the preStop majority guard, which was backwards: it computed a
   majority threshold from the *pre-removal* voter count and blocked
   the last safe removal of a 2-voter quorum (2 -> 1), leaving a dead
   quorum with only 1 live member. Removing a departing voter can only
   ever lower the majority threshold for the remaining set, and
   remove-controller itself can't corrupt anything if peers are
   unreachable (the call just fails). The only real invariant is
   "never remove the last voter" - replaced the condition accordingly
   and updated the doc comment, log message and tests.

3. Raise the quorum-manager sidecar's resource limits: kafka-run-class.sh
   defaults KAFKA_HEAP_OPTS to -Xmx256M when unset, which could already
   exceed the old 128Mi memory limit before JVM/metaspace/SSL overhead.
   Set an explicit -Xmx128M, raise memory to 256Mi request / 512Mi
   limit, and raise CPU to 500m limit so a JVM cold start, SSL
   handshake and admin round-trip fit inside the sidecar's existing
   15s/25s timeouts.

4. Stop the sidecar's render/merge preamble from crash-looping the
   container: with OrderedReady pod management now applying to every
   non-Kerberos controller StatefulSet, a crash-looping sidecar makes
   its pod NotReady and blocks scale/update progress for every sibling
   pod in the role. The preamble's inputs are static, operator-rendered
   config, so retrying won't help a genuine misconfiguration. On
   failure it now falls into a degraded loop that logs a clear error
   every 30s and never attempts add-controller, keeping the container
   Running while staying visible via kubectl logs.

5. Document the --initial-controllers scale-up procedure as a known,
   tracked risk in kraft-controller.adoc's Known Issues: new
   controllers are formatted with the full desired voter set rather
   than Kafka's documented join-existing-quorum procedure. Confirmed
   working in manual live testing but not yet verified end-to-end
   through the fully automated sidecar path.

Also (optional fixes from the review):
- config_map.rs: added a dedicated ClientProperties error variant,
  used by both client.properties and admin-client.properties
  serialization, instead of reusing the misleading
  JvmSecurityPropertiesSnafu context.
- Added a targeted assertion that the quorum-manager sidecar carries
  NODE_ID_OFFSET, consumed directly by its EXPORT_REPLICA_ID bash
  logic under set -u.

Verified: cargo build/test/clippy/fmt all clean, make regenerate-charts
produces no diff (no CRD schema changes in this fix wave).

Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
@razvan razvan self-assigned this Aug 14, 2026
razvan and others added 9 commits August 17, 2026 11:25
…ecar's job

Per explicit request: adding a new controller to the KRaft voter set is
now solely the concern of the quorum-manager sidecar.

- Remove KafkaPodDescriptor::as_voter(), to_initial_controllers(), and
  initial_controllers_command() entirely.
- Exactly one controller (lowest node_id among all controller pod
  descriptors, stable across replica-count changes of an existing role
  group) formats with kafka-storage.sh format --standalone, bootstrapping
  a single-node quorum by itself. Every other controller -- whether part
  of the initial desired replica count or added later on scale-up --
  formats with --no-initial-controllers and joins purely through the
  sidecar's existing add-controller loop. Brokers always format with
  --no-initial-controllers too (never voters, never the bootstrap
  candidate).

- Found and fixed, via live testing, a second instance of the same
  pod-template-churn problem: the sidecar's own command also baked in the
  full peer list as a Rust-level literal, so it changed with replica
  count too. Fixed by reading controller.quorum.bootstrap.servers at
  runtime from the already-mounted, already-fully-resolved (no
  ${env:...} placeholders) controller.properties ConfigMap file instead.

- Found and fixed, via live testing, a genuine previously-undiscovered
  blocking bug: the quorum-manager sidecar never mounted the data volume,
  so add-controller could never read its own meta.properties to identify
  itself -- every attempt failed with "Unable to read meta.properties
  from /stackable/data/kraft". The feature could not have worked at all
  without this. Confirmed live after the fix: both joiners log "Added
  controller ... with directory id ..." and reach follower/leader state.

- Confirmed live: the standalone bootstrap node self-heals correctly
  after an unplanned restart (removes itself via preStop, rejoins via
  add-controller once back up) -- validates the design's resilience
  under a real, non-scale-down termination.

- Confirmed live (and locked in with a regression test): the container
  command for both the kafka container and the sidecar is now identical
  regardless of replica count. A platform-level ConfigMap-restarter
  mechanism (unrelated to this change, ubiquitous across Stackable
  operators) still restarts every controller pod on scale today, because
  controller.quorum.bootstrap.servers still legitimately changes content
  with replica count -- documented as a known issue rather than chased
  further, per explicit instruction.

- Clean up now-genuinely-dead 3.7.x jinja guards in the operations-kraft
  kuttl test templates and README (3.7 support was fully removed in the
  prior commit; these were stale leftovers).

Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
Root cause, confirmed both in isolation and live: GNU `timeout N cmd`
without `--kill-after` only *sends* SIGTERM once N seconds elapse -- it
does not force-kill `cmd`, so if the wrapped process doesn't honor the
signal promptly, the call can run far longer than N seconds.

Confirmed directly, independent of Kafka:

    $ time timeout 3 bash -c 'trap "" TERM; sleep 30'
    real  0m30.004s     # not 3s

    $ time timeout --kill-after=2 3 bash -c 'trap "" TERM; sleep 30'
    Killed
    real  0m5.001s      # correctly bounded

Confirmed live: during a full namespace deletion (every controller
terminating concurrently, so a peer's describe/add-controller/
remove-controller call can hit a blackholed rather than actively-refused
connection), a controller's quorum-manager sidecar kept running well past
its own preStop script's ~25-40s design budget, because none of its
`timeout 15 kafka-metadata-quorum.sh ...` calls were actually bounded to
15 seconds.

Fix: add `--kill-after=5` (CLI_CALL_KILL_AFTER_SECONDS) to every
kafka-metadata-quorum.sh invocation in both the sidecar's main loop
(add-controller) and its preStop hook (describe/remove-controller), so a
hung call is now genuinely force-killed shortly after its nominal
timeout, regardless of what the JVM does internally.

Verified live after the fix: a controller pod's own SIGTERM-to-fully-gone
transition is now a few seconds, not 100+. The residual multi-minute
total for a full 3-controller namespace teardown is now dominated by a
separate, distinct cause (OrderedReady serializing pod-by-pod deletion
even during full teardown, compounded by apparent slowness in this
long-running test environment) -- reported separately, not fixed here.

Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
…ervice

controller.quorum.bootstrap.servers is required by the real Kafka server
process (both kafka and broker containers) whenever using KRaft's dynamic
quorum mode -- it's not removable, per kafka-storage.sh format --help,
which requires it in place of controller.quorum.voters for all three
dynamic-quorum modes. But its *value* didn't need to enumerate every
current controller pod.

kraft_controllers() now emits one address per distinct controller role
group, pointing at that role group's own headless Service DNS name
(no pod prefix) instead of individual pod FQDNs. A headless Service's own
DNS name resolves to every backing pod's IP -- exactly what Kafka's own
client.dns.lookup=use_all_dns_ips default already expects -- and the
operator's headless Service already sets publishNotReadyAddresses: true,
so this also resolves correctly during initial cluster formation before
any pod is Ready.

This also benefits the quorum-manager sidecar, which reads this same
ConfigMap value at runtime for its own --bootstrap-controller argument.

Removed KafkaPodDescriptor::fqdn()/pod_name(), now genuinely unused (their
only remaining caller was the per-pod address construction this replaces).

Verified live, both initial formation and scale-up:
- A fresh 3-controller cluster bootstrapped correctly end-to-end using
  only the service-level bootstrap address (no per-pod list at all);
  kafka-metadata-quorum.sh describe --replication against just the
  service name correctly reached the leader and showed all 3 as real
  voters.
- Scaling 3->5 left every existing controller pod completely untouched:
  identical UIDs, zero restarts, and the StatefulSet's currentRevision/
  updateRevision hash did not change at all (previously this scale
  operation restarted every existing controller pod, both via the
  container-command churn fixed earlier and via the platform's
  ConfigMap-restarter reacting to this exact property's changing content
  -- both causes are now eliminated).

Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
The Stackable platform's admission control rejects any container whose
memory limit-to-request ratio isn't exactly 1, which the operator's own
builder had already been warning about (visible throughout this branch's
live testing: "memory max limit to request ratio for Container
quorum-manager is 1.0, but ratio was exceeded"). The smoke-kraft test
cluster enforces this, so the earlier fix that raised the sidecar's
memory limit to 512Mi while leaving the request at 256Mi (a 2:1 ratio)
made the controller StatefulSet's pods entirely uncreatable there:

  Warning FailedCreate: pods "test-kafka-controller-automatic-log-config-0"
  is forbidden: memory max limit to request ratio per Container is 1,
  but provided ratio is 2.000000

Fixed by setting the request to 512Mi too (matching the limit), rather
than lowering the limit -- the original reasoning for 512Mi still holds
(a JVM cold start plus SSL handshake plus admin round-trip inside the
sidecar's existing timeout/preStop budgets).

Added a regression test asserting every container in both the broker and
controller pods has memory request == memory limit, so a future container
addition can't reintroduce this for either role.

Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
Confirmed live on a 3-controller cluster: a controller pod that is the
last remaining voter at termination time (scaling controllers down to
1, or the last survivor of a full namespace teardown) hit the "would
leave zero voters" branch of the preStop guard, which had no early
exit -- it kept retrying every 2s until the full 25s DEADLINE elapsed,
for no benefit, since nothing else adds a voter for this pod while it
is terminating and the outcome can never change.

Compared against a normal removal (2+ remaining voters), which
completes in 5-8s: the last-voter case always cost the full 25s
before even reaching SIGTERM.

Fixed by breaking out of the retry loop immediately once "would leave
zero voters" is detected, instead of falling through to sleep/retry.

Also confirmed but NOT root-caused or fixed here: in the same live
test, the pod continued sitting in Terminating for several more
minutes after the preStop hook itself finished. The sidecar's own
`trap 'exit 0' TERM` fires instantly once SIGTERM actually reaches it
(confirmed by sending it manually via `kubectl exec`), so the extra
delay is not caused by this sidecar's signal handling -- something
external delayed the SIGTERM being delivered at all in that run. That
test bypassed the operator (StatefulSet was patched directly with the
operator paused, to capture preStop's own output, which Kubernetes
does not surface via `kubectl logs`), so it isn't yet confirmed this
reproduces in normal operation; flagging as a known open question
rather than guessing at a fix.

Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
Merging in the better KRaft controller readiness/liveness probes from
PR #1006 since they're a better fit as part of this dynamic voter
membership branch than as a standalone PR. The author will close
#1006 separately.

# Conflicts:
#	CHANGELOG.md
#	docs/modules/kafka/pages/usage-guide/kraft-controller.adoc
#	rust/operator-binary/src/controller/build/resource/statefulset.rs
Confirmed from a live report: scaling a KRaft cluster's only controller
role group down to 0 replicas while brokers kept running passed
validation and failed much later, and far more confusingly, while
building the *broker* role group's ConfigMap:

  failed to build ConfigMap for role group default, no Kraft
  controllers found to build

That happened because the broker ConfigMap's render also reads the
controller quorum's pod descriptors (for
controller.quorum.bootstrap.servers), which come back empty once there
are zero controller replicas -- an unrelated role group's build failing
as a side effect of the real problem, with a message that doesn't name
it. Brokers with zero controllers have no metadata quorum to talk to at
all, so this combination is now rejected up front in the validate step,
summed across all controller role groups (so e.g. two role groups at
0+0 replicas each are also caught).

Deliberately narrower than "controllers can never be zero": controllers
*and* brokers at zero together is allowed, since that's exactly what
`clusterOperation.stopped` already does today, unconditionally, for
every Stackable operator (scaling every managed StatefulSet's replicas
to 0 at apply time, bypassing this validate-time check entirely) -- a
coordinated whole-cluster stop is already a supported shape, not one
this check should forbid.

`replicas: unset` (left for a HorizontalPodAutoscaler to own) is not
treated as zero for either role. ZooKeeper-mode clusters are
unaffected -- their controllers aren't a metadata quorum.

Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
…longer panics/errors

The zero-controller-replicas validate check (342e503) allowed controllers
and brokers scaled to 0 together, but two downstream build steps still
assumed at least one controller pod descriptor existed whenever KRaft
mode was active, regardless of that role group's own replica count:

- build_rolegroup_config_map hard-errored (NoKraftControllersFound) on
  any empty pod descriptor list in KRaft mode.
- controller_quorum_format_flag panicked via .expect() when computing
  the lowest controller node id from an empty descriptor list.

Both StatefulSets and ConfigMaps are still built for 0-replica role
groups (just never scheduled), so a whole-cluster-at-zero build must
succeed even with an empty descriptor list. Confirmed via the
operations-kraft kuttl test's controller-shutdown step, which scales
both roles to 0 in the same apply and previously failed reconciliation
with 'no Kraft controllers found to build'.

Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
@razvan

razvan commented Aug 18, 2026

Copy link
Copy Markdown
Member Author

closing in favor of a new, cleaner and hopefully easier to review pr

@razvan razvan closed this Aug 18, 2026
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