queue-aiops 0.2.1__tar.gz → 0.2.2__tar.gz
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- {queue_aiops-0.2.1 → queue_aiops-0.2.2}/PKG-INFO +5 -4
- {queue_aiops-0.2.1 → queue_aiops-0.2.2}/README.md +4 -3
- queue_aiops-0.2.2/RELEASE_NOTES.md +29 -0
- {queue_aiops-0.2.1 → queue_aiops-0.2.2}/pyproject.toml +1 -1
- {queue_aiops-0.2.1 → queue_aiops-0.2.2}/queue_aiops/ops/rabbit_reads.py +15 -15
- {queue_aiops-0.2.1 → queue_aiops-0.2.2}/queue_aiops/ops/redis_reads.py +4 -4
- {queue_aiops-0.2.1 → queue_aiops-0.2.2}/server.json +2 -2
- {queue_aiops-0.2.1 → queue_aiops-0.2.2}/uv.lock +1 -1
- queue_aiops-0.2.1/RELEASE_NOTES.md +0 -26
- {queue_aiops-0.2.1 → queue_aiops-0.2.2}/.github/workflows/mcp-publish.yml +0 -0
- {queue_aiops-0.2.1 → queue_aiops-0.2.2}/.github/workflows/publish.yml +0 -0
- {queue_aiops-0.2.1 → queue_aiops-0.2.2}/.gitignore +0 -0
- {queue_aiops-0.2.1 → queue_aiops-0.2.2}/CHANGELOG.md +0 -0
- {queue_aiops-0.2.1 → queue_aiops-0.2.2}/LICENSE +0 -0
- {queue_aiops-0.2.1 → queue_aiops-0.2.2}/SECURITY.md +0 -0
- {queue_aiops-0.2.1 → queue_aiops-0.2.2}/docs/VERIFICATION.md +0 -0
- {queue_aiops-0.2.1 → queue_aiops-0.2.2}/mcp_server/__init__.py +0 -0
- {queue_aiops-0.2.1 → queue_aiops-0.2.2}/mcp_server/_shared.py +0 -0
- {queue_aiops-0.2.1 → queue_aiops-0.2.2}/mcp_server/server.py +0 -0
- {queue_aiops-0.2.1 → queue_aiops-0.2.2}/mcp_server/tools/__init__.py +0 -0
- {queue_aiops-0.2.1 → queue_aiops-0.2.2}/mcp_server/tools/analysis.py +0 -0
- {queue_aiops-0.2.1 → queue_aiops-0.2.2}/mcp_server/tools/overview.py +0 -0
- {queue_aiops-0.2.1 → queue_aiops-0.2.2}/mcp_server/tools/rabbit_reads.py +0 -0
- {queue_aiops-0.2.1 → queue_aiops-0.2.2}/mcp_server/tools/redis_reads.py +0 -0
- {queue_aiops-0.2.1 → queue_aiops-0.2.2}/mcp_server/tools/undo.py +0 -0
- {queue_aiops-0.2.1 → queue_aiops-0.2.2}/mcp_server/tools/writes.py +0 -0
- {queue_aiops-0.2.1 → queue_aiops-0.2.2}/queue_aiops/__init__.py +0 -0
- {queue_aiops-0.2.1 → queue_aiops-0.2.2}/queue_aiops/cli/__init__.py +0 -0
- {queue_aiops-0.2.1 → queue_aiops-0.2.2}/queue_aiops/cli/_common.py +0 -0
- {queue_aiops-0.2.1 → queue_aiops-0.2.2}/queue_aiops/cli/_root.py +0 -0
- {queue_aiops-0.2.1 → queue_aiops-0.2.2}/queue_aiops/cli/analyze.py +0 -0
- {queue_aiops-0.2.1 → queue_aiops-0.2.2}/queue_aiops/cli/doctor.py +0 -0
- {queue_aiops-0.2.1 → queue_aiops-0.2.2}/queue_aiops/cli/init.py +0 -0
- {queue_aiops-0.2.1 → queue_aiops-0.2.2}/queue_aiops/cli/overview.py +0 -0
- {queue_aiops-0.2.1 → queue_aiops-0.2.2}/queue_aiops/cli/rabbit_cmds.py +0 -0
- {queue_aiops-0.2.1 → queue_aiops-0.2.2}/queue_aiops/cli/redis_cmds.py +0 -0
- {queue_aiops-0.2.1 → queue_aiops-0.2.2}/queue_aiops/cli/secret.py +0 -0
- {queue_aiops-0.2.1 → queue_aiops-0.2.2}/queue_aiops/cli/undo.py +0 -0
- {queue_aiops-0.2.1 → queue_aiops-0.2.2}/queue_aiops/config.py +0 -0
- {queue_aiops-0.2.1 → queue_aiops-0.2.2}/queue_aiops/connection.py +0 -0
- {queue_aiops-0.2.1 → queue_aiops-0.2.2}/queue_aiops/doctor.py +0 -0
- {queue_aiops-0.2.1 → queue_aiops-0.2.2}/queue_aiops/governance/__init__.py +0 -0
- {queue_aiops-0.2.1 → queue_aiops-0.2.2}/queue_aiops/governance/audit.py +0 -0
- {queue_aiops-0.2.1 → queue_aiops-0.2.2}/queue_aiops/governance/budget.py +0 -0
- {queue_aiops-0.2.1 → queue_aiops-0.2.2}/queue_aiops/governance/decorators.py +0 -0
- {queue_aiops-0.2.1 → queue_aiops-0.2.2}/queue_aiops/governance/paths.py +0 -0
- {queue_aiops-0.2.1 → queue_aiops-0.2.2}/queue_aiops/governance/patterns.py +0 -0
- {queue_aiops-0.2.1 → queue_aiops-0.2.2}/queue_aiops/governance/policy.py +0 -0
- {queue_aiops-0.2.1 → queue_aiops-0.2.2}/queue_aiops/governance/readonly.py +0 -0
- {queue_aiops-0.2.1 → queue_aiops-0.2.2}/queue_aiops/governance/sanitize.py +0 -0
- {queue_aiops-0.2.1 → queue_aiops-0.2.2}/queue_aiops/governance/undo.py +0 -0
- {queue_aiops-0.2.1 → queue_aiops-0.2.2}/queue_aiops/ops/__init__.py +0 -0
- {queue_aiops-0.2.1 → queue_aiops-0.2.2}/queue_aiops/ops/_util.py +0 -0
- {queue_aiops-0.2.1 → queue_aiops-0.2.2}/queue_aiops/ops/analysis.py +0 -0
- {queue_aiops-0.2.1 → queue_aiops-0.2.2}/queue_aiops/ops/overview.py +0 -0
- {queue_aiops-0.2.1 → queue_aiops-0.2.2}/queue_aiops/ops/writes.py +0 -0
- {queue_aiops-0.2.1 → queue_aiops-0.2.2}/queue_aiops/platform.py +0 -0
- {queue_aiops-0.2.1 → queue_aiops-0.2.2}/queue_aiops/secretstore.py +0 -0
- {queue_aiops-0.2.1 → queue_aiops-0.2.2}/skills/queue-aiops/SKILL.md +0 -0
- {queue_aiops-0.2.1 → queue_aiops-0.2.2}/skills/queue-aiops/references/agent-guardrails.md +0 -0
- {queue_aiops-0.2.1 → queue_aiops-0.2.2}/skills/queue-aiops/references/capabilities.md +0 -0
- {queue_aiops-0.2.1 → queue_aiops-0.2.2}/skills/queue-aiops/references/cli-reference.md +0 -0
- {queue_aiops-0.2.1 → queue_aiops-0.2.2}/skills/queue-aiops/references/setup-guide.md +0 -0
- {queue_aiops-0.2.1 → queue_aiops-0.2.2}/smithery.yaml +0 -0
- {queue_aiops-0.2.1 → queue_aiops-0.2.2}/tests/conftest.py +0 -0
- {queue_aiops-0.2.1 → queue_aiops-0.2.2}/tests/fakes.py +0 -0
- {queue_aiops-0.2.1 → queue_aiops-0.2.2}/tests/test_analysis.py +0 -0
- {queue_aiops-0.2.1 → queue_aiops-0.2.2}/tests/test_cli_rabbit_writes.py +0 -0
- {queue_aiops-0.2.1 → queue_aiops-0.2.2}/tests/test_cli_reads.py +0 -0
- {queue_aiops-0.2.1 → queue_aiops-0.2.2}/tests/test_cli_secret.py +0 -0
- {queue_aiops-0.2.1 → queue_aiops-0.2.2}/tests/test_cli_writes.py +0 -0
- {queue_aiops-0.2.1 → queue_aiops-0.2.2}/tests/test_connection.py +0 -0
- {queue_aiops-0.2.1 → queue_aiops-0.2.2}/tests/test_doctor.py +0 -0
- {queue_aiops-0.2.1 → queue_aiops-0.2.2}/tests/test_gov_audit.py +0 -0
- {queue_aiops-0.2.1 → queue_aiops-0.2.2}/tests/test_gov_decorators.py +0 -0
- {queue_aiops-0.2.1 → queue_aiops-0.2.2}/tests/test_gov_patterns.py +0 -0
- {queue_aiops-0.2.1 → queue_aiops-0.2.2}/tests/test_gov_policy.py +0 -0
- {queue_aiops-0.2.1 → queue_aiops-0.2.2}/tests/test_governance_persistence.py +0 -0
- {queue_aiops-0.2.1 → queue_aiops-0.2.2}/tests/test_init.py +0 -0
- {queue_aiops-0.2.1 → queue_aiops-0.2.2}/tests/test_mcp_reads.py +0 -0
- {queue_aiops-0.2.1 → queue_aiops-0.2.2}/tests/test_optional_fields.py +0 -0
- {queue_aiops-0.2.1 → queue_aiops-0.2.2}/tests/test_platform.py +0 -0
- {queue_aiops-0.2.1 → queue_aiops-0.2.2}/tests/test_readonly.py +0 -0
- {queue_aiops-0.2.1 → queue_aiops-0.2.2}/tests/test_reads.py +0 -0
- {queue_aiops-0.2.1 → queue_aiops-0.2.2}/tests/test_secretstore.py +0 -0
- {queue_aiops-0.2.1 → queue_aiops-0.2.2}/tests/test_smoke.py +0 -0
- {queue_aiops-0.2.1 → queue_aiops-0.2.2}/tests/test_undo_executor.py +0 -0
- {queue_aiops-0.2.1 → queue_aiops-0.2.2}/tests/test_writes.py +0 -0
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
Metadata-Version: 2.4
|
|
2
2
|
Name: queue-aiops
|
|
3
|
-
Version: 0.2.
|
|
3
|
+
Version: 0.2.2
|
|
4
4
|
Summary: Governed AI-ops for redis + rabbitmq: memory-pressure/latency/queue-backlog/connection-churn RCAs, slowlog and SCAN-budgeted big-key sampling, queues/policies/nodes, and governed writes (config set, client kill, purge/delete queue, policies) with a built-in governance harness (audit, budget, undo, risk tiers)
|
|
5
5
|
Author-email: wei <zhouwei008@gmail.com>
|
|
6
6
|
License-Expression: MIT
|
|
@@ -192,15 +192,16 @@ queue-aiops analyze memory|latency|backlog|churn
|
|
|
192
192
|
|
|
193
193
|
## Verification status
|
|
194
194
|
|
|
195
|
-
**Live-verified against Redis 7.4.9 (2026-07-19)
|
|
195
|
+
**Live-verified against Redis 7.4.9 and RabbitMQ 3.13.7 (2026-07-19/20).**
|
|
196
196
|
Connectivity, every Redis read (cross-checked against `redis-cli` ground truth), all
|
|
197
197
|
four analyses, and the full governance loop (real `redis_config_set` → audit row →
|
|
198
198
|
undo restoring the prior value) were exercised against a real server. That run found
|
|
199
199
|
and fixed a real defect: integer quantities — key counts, client counts, byte totals —
|
|
200
200
|
were rendered as floats (`202.0` keys), which equality assertions cannot catch.
|
|
201
201
|
|
|
202
|
-
The
|
|
203
|
-
|
|
202
|
+
The RabbitMQ group is now verified end-to-end too — reads cross-checked against
|
|
203
|
+
`rabbitmqadmin`, and `set_policy` → `undo_apply` closing on the live broker. **Redis
|
|
204
|
+
cluster/sentinel topologies and AUTH/TLS connections** remain unverified.
|
|
204
205
|
|
|
205
206
|
[docs/VERIFICATION.md](docs/VERIFICATION.md) records exactly what was checked and what
|
|
206
207
|
is still open. `queue-aiops doctor` is the fastest live check.
|
|
@@ -175,15 +175,16 @@ queue-aiops analyze memory|latency|backlog|churn
|
|
|
175
175
|
|
|
176
176
|
## Verification status
|
|
177
177
|
|
|
178
|
-
**Live-verified against Redis 7.4.9 (2026-07-19)
|
|
178
|
+
**Live-verified against Redis 7.4.9 and RabbitMQ 3.13.7 (2026-07-19/20).**
|
|
179
179
|
Connectivity, every Redis read (cross-checked against `redis-cli` ground truth), all
|
|
180
180
|
four analyses, and the full governance loop (real `redis_config_set` → audit row →
|
|
181
181
|
undo restoring the prior value) were exercised against a real server. That run found
|
|
182
182
|
and fixed a real defect: integer quantities — key counts, client counts, byte totals —
|
|
183
183
|
were rendered as floats (`202.0` keys), which equality assertions cannot catch.
|
|
184
184
|
|
|
185
|
-
The
|
|
186
|
-
|
|
185
|
+
The RabbitMQ group is now verified end-to-end too — reads cross-checked against
|
|
186
|
+
`rabbitmqadmin`, and `set_policy` → `undo_apply` closing on the live broker. **Redis
|
|
187
|
+
cluster/sentinel topologies and AUTH/TLS connections** remain unverified.
|
|
187
188
|
|
|
188
189
|
[docs/VERIFICATION.md](docs/VERIFICATION.md) records exactly what was checked and what
|
|
189
190
|
is still open. `queue-aiops doctor` is the fastest live check.
|
|
@@ -0,0 +1,29 @@
|
|
|
1
|
+
# Release notes — queue-aiops 0.2.2
|
|
2
|
+
|
|
3
|
+
Previous release: 0.2.1.
|
|
4
|
+
|
|
5
|
+
## Fixed: the remaining float-typed integer quantities
|
|
6
|
+
|
|
7
|
+
0.2.1 converted the obvious counts, but a regex-driven sweep missed 19 more —
|
|
8
|
+
`consumers`, per-node `fdUsed`/`socketsUsed`, `queues`/`connections`/`channels`
|
|
9
|
+
object totals, connection `connectedAt`, slowlog `id`/`startTime`/`durationUs`, and
|
|
10
|
+
Redis `totalCommands`. A live RabbitMQ broker reported `"consumers": 0.0`.
|
|
11
|
+
|
|
12
|
+
These are integers now. Genuinely fractional values — `consumerUtilisation`,
|
|
13
|
+
`opsPerSec`, `fragmentationRatio`, and the `*Rate` fields — are unchanged.
|
|
14
|
+
|
|
15
|
+
## Live-verified: RabbitMQ
|
|
16
|
+
|
|
17
|
+
The entire RabbitMQ command group had never been run against a live broker. It has
|
|
18
|
+
now been exercised against **RabbitMQ 3.13.7**: `doctor`, every read
|
|
19
|
+
(`overview`, `queues`, `queue`, `connections`, `channels`, `policies`, `nodes`) with
|
|
20
|
+
queue depth cross-checked against `rabbitmqadmin` ground truth, the backlog and
|
|
21
|
+
churn analyses, and the full governance loop — `set_policy` really created a policy
|
|
22
|
+
on the broker, `priorState` recorded that it had not existed, and `undo_apply`
|
|
23
|
+
deleted it, with all three calls audited.
|
|
24
|
+
|
|
25
|
+
The platform guard was confirmed too: a Redis-only analysis against a RabbitMQ
|
|
26
|
+
target fails with a teaching error naming the mismatch.
|
|
27
|
+
|
|
28
|
+
See [docs/VERIFICATION.md](docs/VERIFICATION.md). Redis cluster/sentinel topologies
|
|
29
|
+
and AUTH/TLS connections remain unverified.
|
|
@@ -4,7 +4,7 @@ build-backend = "hatchling.build"
|
|
|
4
4
|
|
|
5
5
|
[project]
|
|
6
6
|
name = "queue-aiops"
|
|
7
|
-
version = "0.2.
|
|
7
|
+
version = "0.2.2"
|
|
8
8
|
description = "Governed AI-ops for redis + rabbitmq: memory-pressure/latency/queue-backlog/connection-churn RCAs, slowlog and SCAN-budgeted big-key sampling, queues/policies/nodes, and governed writes (config set, client kill, purge/delete queue, policies) with a built-in governance harness (audit, budget, undo, risk tiers)"
|
|
9
9
|
readme = "README.md"
|
|
10
10
|
license = "MIT"
|
|
@@ -31,10 +31,10 @@ def broker_overview(conn: Any) -> dict:
|
|
|
31
31
|
"messagesReady": as_int(totals.get("messages_ready")),
|
|
32
32
|
"messagesUnacked": as_int(totals.get("messages_unacknowledged")),
|
|
33
33
|
"messagesTotal": as_int(totals.get("messages")),
|
|
34
|
-
"queues":
|
|
35
|
-
"connections":
|
|
36
|
-
"channels":
|
|
37
|
-
"consumers":
|
|
34
|
+
"queues": as_int(objects.get("queues")),
|
|
35
|
+
"connections": as_int(objects.get("connections")),
|
|
36
|
+
"channels": as_int(objects.get("channels")),
|
|
37
|
+
"consumers": as_int(objects.get("consumers")),
|
|
38
38
|
"publishRate": rate(stats.get("publish_details")),
|
|
39
39
|
"deliverRate": rate(stats.get("deliver_get_details")),
|
|
40
40
|
"ackRate": rate(stats.get("ack_details")),
|
|
@@ -59,7 +59,7 @@ def _queue_row(q: dict) -> dict:
|
|
|
59
59
|
"messages": as_int(q.get("messages")),
|
|
60
60
|
"messagesReady": as_int(q.get("messages_ready")),
|
|
61
61
|
"messagesUnacked": as_int(q.get("messages_unacknowledged")),
|
|
62
|
-
"consumers":
|
|
62
|
+
"consumers": as_int(q.get("consumers")),
|
|
63
63
|
"memoryBytes": as_int(q.get("memory")),
|
|
64
64
|
"publishRate": rate(stats.get("publish_details")),
|
|
65
65
|
"deliverRate": rate(stats.get("deliver_get_details")),
|
|
@@ -123,8 +123,8 @@ def list_connections(conn: Any) -> dict:
|
|
|
123
123
|
"peerHost": opt(pick(c, "peer_host", default="(unknown)"), 64),
|
|
124
124
|
"user": opt(c.get("user"), 64),
|
|
125
125
|
"state": opt(c.get("state"), 32),
|
|
126
|
-
"channels":
|
|
127
|
-
"connectedAt":
|
|
126
|
+
"channels": as_int(c.get("channels")),
|
|
127
|
+
"connectedAt": as_int(c.get("connected_at")),
|
|
128
128
|
"clientProduct": s(as_obj(c.get("client_properties")).get("product"), 64),
|
|
129
129
|
}
|
|
130
130
|
for c in conn.platform.rows(conn.get(conn.platform.path("connections")))
|
|
@@ -158,9 +158,9 @@ def list_channels(conn: Any) -> dict:
|
|
|
158
158
|
"connection": s(as_obj(c.get("connection_details")).get("name"), 128),
|
|
159
159
|
"user": opt(c.get("user"), 64),
|
|
160
160
|
"state": opt(c.get("state"), 32),
|
|
161
|
-
"unacked":
|
|
162
|
-
"prefetch":
|
|
163
|
-
"consumers":
|
|
161
|
+
"unacked": as_int(c.get("messages_unacknowledged")),
|
|
162
|
+
"prefetch": as_int(c.get("prefetch_count")),
|
|
163
|
+
"consumers": as_int(c.get("consumer_count")),
|
|
164
164
|
}
|
|
165
165
|
for c in conn.platform.rows(conn.get(conn.platform.path("channels")))
|
|
166
166
|
]
|
|
@@ -190,7 +190,7 @@ def list_policies(conn: Any, vhost: str | None = None) -> dict:
|
|
|
190
190
|
"vhost": opt(p.get("vhost"), 64),
|
|
191
191
|
"pattern": opt(p.get("pattern"), 128),
|
|
192
192
|
"applyTo": opt(pick(p, "apply-to", "apply_to", default="all"), 32),
|
|
193
|
-
"priority":
|
|
193
|
+
"priority": as_int(p.get("priority")),
|
|
194
194
|
"definition": as_obj(p.get("definition")),
|
|
195
195
|
}
|
|
196
196
|
for p in conn.platform.rows(conn.get(path))
|
|
@@ -223,10 +223,10 @@ def node_health(conn: Any) -> dict:
|
|
|
223
223
|
"diskFreeBytes": as_int(n.get("disk_free")),
|
|
224
224
|
"diskFreeLimitBytes": as_int(n.get("disk_free_limit")),
|
|
225
225
|
"diskAlarm": bool(n.get("disk_free_alarm")),
|
|
226
|
-
"fdUsed":
|
|
227
|
-
"fdTotal":
|
|
228
|
-
"socketsUsed":
|
|
229
|
-
"socketsTotal":
|
|
226
|
+
"fdUsed": as_int(n.get("fd_used")),
|
|
227
|
+
"fdTotal": as_int(n.get("fd_total")),
|
|
228
|
+
"socketsUsed": as_int(n.get("sockets_used")),
|
|
229
|
+
"socketsTotal": as_int(n.get("sockets_total")),
|
|
230
230
|
}
|
|
231
231
|
)
|
|
232
232
|
rows.sort(key=lambda r: r["memUsedBytes"], reverse=True)
|
|
@@ -40,7 +40,7 @@ def server_info(conn: Any) -> dict:
|
|
|
40
40
|
"connectedClients": as_int(info.get("connected_clients")),
|
|
41
41
|
"blockedClients": as_int(info.get("blocked_clients")),
|
|
42
42
|
"opsPerSec": num(info.get("instantaneous_ops_per_sec")),
|
|
43
|
-
"totalCommands":
|
|
43
|
+
"totalCommands": as_int(info.get("total_commands_processed")),
|
|
44
44
|
"keyspaceHits": as_int(info.get("keyspace_hits")),
|
|
45
45
|
"keyspaceMisses": as_int(info.get("keyspace_misses")),
|
|
46
46
|
"hitRatePct": pct(
|
|
@@ -143,9 +143,9 @@ def slowlog(conn: Any, count: int = MAX_SLOWLOG_ROWS) -> dict:
|
|
|
143
143
|
entries = conn.redis_slowlog(want + 1)
|
|
144
144
|
rows = [
|
|
145
145
|
{
|
|
146
|
-
"id":
|
|
147
|
-
"startTime":
|
|
148
|
-
"durationUs":
|
|
146
|
+
"id": as_int(e.get("id")),
|
|
147
|
+
"startTime": as_int(e.get("start_time")),
|
|
148
|
+
"durationUs": as_int(e.get("duration")),
|
|
149
149
|
"command": _command_text(e.get("command")),
|
|
150
150
|
"clientAddr": opt(e.get("client_address"), 64),
|
|
151
151
|
"clientName": opt(e.get("client_name"), 64),
|
|
@@ -7,12 +7,12 @@
|
|
|
7
7
|
"url": "https://github.com/AIops-tools/Queue-AIops",
|
|
8
8
|
"source": "github"
|
|
9
9
|
},
|
|
10
|
-
"version": "0.2.
|
|
10
|
+
"version": "0.2.2",
|
|
11
11
|
"packages": [
|
|
12
12
|
{
|
|
13
13
|
"registryType": "pypi",
|
|
14
14
|
"identifier": "queue-aiops",
|
|
15
|
-
"version": "0.2.
|
|
15
|
+
"version": "0.2.2",
|
|
16
16
|
"transport": {
|
|
17
17
|
"type": "stdio"
|
|
18
18
|
}
|
|
@@ -1,26 +0,0 @@
|
|
|
1
|
-
# Release notes — queue-aiops 0.2.1
|
|
2
|
-
|
|
3
|
-
Previous release: 0.2.0.
|
|
4
|
-
|
|
5
|
-
## Fixed: integer quantities were rendered as floats
|
|
6
|
-
|
|
7
|
-
Key counts, client counts and byte totals were routed through the float coercion
|
|
8
|
-
helper, so a live server reported `202.0` keys and `1.0` connected clients. The
|
|
9
|
-
values were arithmetically right but semantically wrong — a count cannot be
|
|
10
|
-
fractional, and a reader should not have to wonder whether `.0` means the number was
|
|
11
|
-
rounded.
|
|
12
|
-
|
|
13
|
-
These now use a new `as_int()` helper and come back as integers. Genuinely fractional
|
|
14
|
-
values (`hitRatePct`, `usedPctOfMax`, `opsPerSec`, `expiresPct`) are unchanged. Note
|
|
15
|
-
that equality assertions cannot catch this class of bug (`202 == 202.0`), so the
|
|
16
|
-
regression test asserts the *type*.
|
|
17
|
-
|
|
18
|
-
If you parse these fields, the JSON shape changes from `202.0` to `202`.
|
|
19
|
-
|
|
20
|
-
## Live-verified
|
|
21
|
-
|
|
22
|
-
This release was exercised end-to-end against a real **Redis 7.4.9** server: every
|
|
23
|
-
Redis read cross-checked against `redis-cli` ground truth, all four analyses, and the
|
|
24
|
-
full governance loop (real `redis_config_set` → audit row → `undo_apply` restoring the
|
|
25
|
-
prior value). See [docs/VERIFICATION.md](docs/VERIFICATION.md) — **RabbitMQ remains
|
|
26
|
-
mock-only** and is now the largest gap in this repo.
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|