)]}'
{
  "log": [
    {
      "commit": "7abf8adaf660d75f2d686bbe8741be76c3a5ddd3",
      "tree": "8f83d295b85a3a1af7fa987d11c2163a7f866597",
      "parents": [
        "3a443e9a15015b5290cf6533568c3de1606d9e07"
      ],
      "author": {
        "name": "lizhimins",
        "email": "707364882@qq.com",
        "time": "Thu Oct 29 16:38:13 2026 +0800"
      },
      "committer": {
        "name": "GitHub",
        "email": "noreply@github.com",
        "time": "Thu Oct 29 16:38:13 2026 +0800"
      },
      "message": "chore(deploy): pin the demo RocketMQ cluster to the released 5.5.0 tag (#7368)\n\nThe demo cluster image was built from the develop branch and tagged\napache-rocketmq:develop, so a release could ship pointing at an unreleased\nmoving target while server/pom.xml declares rocketmq.version 5.5.0.\n\nDefault ROCKETMQ_BRANCH to the released rocketmq-all-5.5.0 tag, expose it as a\ncompose build arg so an override does not need a file edit, and rename the image\ntag to apache-rocketmq:5.5.0 across the six services. Following develop stays\npossible via ROCKETMQ_BRANCH\u003ddevelop for local debugging only."
    },
    {
      "commit": "3a443e9a15015b5290cf6533568c3de1606d9e07",
      "tree": "907c1be32ad303c929d3d63d5cb179962b17563c",
      "parents": [
        "1ebfa8d3a87ab222a95fd622e77772cb4c2d8b58"
      ],
      "author": {
        "name": "lizhimins",
        "email": "707364882@qq.com",
        "time": "Thu Oct 29 15:20:41 2026 +0800"
      },
      "committer": {
        "name": "GitHub",
        "email": "noreply@github.com",
        "time": "Thu Oct 29 15:20:41 2026 +0800"
      },
      "message": "fix(web): Segmented group type in create modal and equal-height audit cards (#7367)\n\nCreate Group modal: the message-type Select offered delay/transaction types\nthat do not apply to consumer groups, plus a delivery-order Select with a\nglobal order the backends do not support. Use the same Segmented control as\nthe create-topic modal with only NORMAL/FIFO, and derive deliveryOrderType\nfrom the chosen type so ordered groups still reach the broker as orderly.\n\nAudit page: the summary statistic cards sized to their content, leaving the\nsuccess-rate card (one extra detail line) 44px taller than its row siblings.\nStretch every card in the summary and risk-insight rows (Row align\u003d\"stretch\"\nplus Card height 100%) so each visual row is even."
    },
    {
      "commit": "1ebfa8d3a87ab222a95fd622e77772cb4c2d8b58",
      "tree": "48e9b99bb3a8c7a3d5ac5a9b03438a4ea38fae71",
      "parents": [
        "fb46b8996aaf886b34023eec5b8e250b5d1c91a1"
      ],
      "author": {
        "name": "lizhimins",
        "email": "707364882@qq.com",
        "time": "Wed Oct 28 18:12:38 2026 +0800"
      },
      "committer": {
        "name": "GitHub",
        "email": "noreply@github.com",
        "time": "Wed Oct 28 18:12:38 2026 +0800"
      },
      "message": "feat(web,ops): users page redesign, instance region filter, audit op names and cluster-alerts hub (#7366)\n\n* feat(web): 用户管理页重设计、实例列表地域筛选与审计高频操作本地化\n\n- 用户管理：修操作列横向溢出裁切；启用/禁用开关归位状态列、角色开关留权限列；\n  列对齐按规范（标签型居中、读数型靠右）；size\u003dsmall 行高 57→41；概览改单行指标条；\n  删除孤立「我的账号」Card，修改我的密码上移页头；会话弹窗加宽至 1300 消除横向滚动，\n  状态标签去「活跃 + 长时间未活跃」矛盾（异常标记优先、活跃作兜底）\n- 实例列表：筛选条加「按地域筛选」（选项取自已加载实例，纯前端过滤）；架构筛选补 aria-label\n- 审计概览：高频操作 / 资源榜改用本地化操作名（创建 Topic 等），每行压成单行；\n  补 5 个告警投递类操作码与 ALERT_NOTIFICATION 资源的呈现和文案\n- 测试：新增对齐 / 列宽 / 地域筛选 / 榜单本地化守护用例，既有断言零破坏\n\n* feat(audit): 审计查询支持多操作码过滤（operationTypes）\n\n- /api/audit-logs 新增逗号串 operationTypes 参数，Controller → Service → Repository\n  的 applyFilters 加 IN 条件；空值传 null，单操作码路径行为不变\n- 前端 AuditQuery 增加 operationTypes（数组逗号拼接传输），供集群事件 Tab 一次取多类控制面操作\n- 同步修正 AuditListToolHandler 与 4 个测试类的调用签名\n\n* feat(ops): 集群告警枢纽页（四 Tab）与集群事件 Tab\n\n- 集群告警 / 告警事件 / 告警投递 三个路由统一渲染枢纽页 clusterAlertsHub（四个 Tab，\n  destroyInactiveTabPane 切走即卸载），侧边栏删掉告警事件、告警投递两项，旧 URL 落到对应 Tab\n- 子页加 embedded 形态：无页头与外层 padding；页头操作区经 portal 挂进 Tab 栏右侧\n  （tabBarExtraContent），与页签同一行，不单独占第二行；批量确认按钮尺寸与同行按钮统一\n- 集群事件 Tab 复用运维审计已记录的控制面变更（Broker / 集群配置修改、重启、Proxy 配置与地址增删），\n  按 operationTypes 多操作码过滤取数；告警事件表操作列加宽至 240 消除右对齐内容左溢遮挡时间列"
    },
    {
      "commit": "fb46b8996aaf886b34023eec5b8e250b5d1c91a1",
      "tree": "278d3516b522eb8c146f5f9f398304b06e48987c",
      "parents": [
        "fb6455a0352e7431144eb355ea66f1000b7fbc55"
      ],
      "author": {
        "name": "lizhimins",
        "email": "707364882@qq.com",
        "time": "Wed Oct 28 13:51:24 2026 +0800"
      },
      "committer": {
        "name": "GitHub",
        "email": "noreply@github.com",
        "time": "Wed Oct 28 13:51:24 2026 +0800"
      },
      "message": "chore(release): harden 3.0.0 release readiness after the trunk audit (#7365)"
    },
    {
      "commit": "fb6455a0352e7431144eb355ea66f1000b7fbc55",
      "tree": "d9a7b674ec4a7170fba9f3c4981aca7ea7557992",
      "parents": [
        "7f2dfd3ed7fd9536b1b46f0efb35a84aadc451a6"
      ],
      "author": {
        "name": "0",
        "email": "1939455790@qq.com",
        "time": "Wed Oct 28 01:08:47 2026 +0800"
      },
      "committer": {
        "name": "GitHub",
        "email": "noreply@github.com",
        "time": "Wed Oct 28 01:08:47 2026 +0800"
      },
      "message": "fix(consumer): keep an unknown total lag from being a partial subtotal (#7053)\n\nWhen the queue scan did not cover every queue, analyzeConsumerGroupHealth fell back\nto the sum of the queues it did see and reported that subtotal as the group\u0027s total\nlag - a partial measurement presented as a complete one. reportedLag now yields null\nunless every queue in the progress list had a readable lag, and the single render\nsite already treats null as unknown. The health-issue path keeps using the known\nsubtotal (summaryReportedLag ?? totalKnownLag), so this changes only what is\nreported as the total, not which issues are raised.\n\nThis complements rather than duplicates the delay-availability work that landed in\n#5918: that change covers delaySeconds, which has no sentinel and needs a boolean\nflag, while totalLag already carries the -1 sentinel, so a single isLagAvailable\nconversion at the boundary is enough. The test file conflicted with those additions;\nboth sides\u0027 cases are kept."
    },
    {
      "commit": "7f2dfd3ed7fd9536b1b46f0efb35a84aadc451a6",
      "tree": "c5de3f851417241fc5b206724d95380d3a181667",
      "parents": [
        "c271c1ef6892ffa4789fd213cc12792dd22067e2"
      ],
      "author": {
        "name": "Yexi Xiang",
        "email": "15986997987@163.com",
        "time": "Wed Oct 28 01:08:40 2026 +0800"
      },
      "committer": {
        "name": "GitHub",
        "email": "noreply@github.com",
        "time": "Wed Oct 28 01:08:40 2026 +0800"
      },
      "message": "fix(metrics,litetopic): stop cancelled jobs after checkpoints (#7188)\n\n* fix(metrics): stop cancelled jobs after checkpoints (#7188)\nA cancelled collection pass was only checked at the persist entry point, so a job\ncancelled while it was blocked in a checkpoint went on to write its samples and\nrun the alert side effects afterwards. Re-check cancellation and the heartbeat\nlease at each of the three points - before the lease refresh, before the snapshot\nwrite, and before alert processing - so a late result from a cancelled or\nde-leased pass is discarded instead of committed. The checks are deliberately\nredundant: each one covers a different interleaving.\n* fix(litetopic): exclude unrelated group clients from summaries (#7203)\nbuildSummary counted every client connected to the consumer group, including\nclients that had no LiteTopic under the parent topic, so the consumer count was\nlarger than the number of groups actually consuming lite topics. Resolve the\nclient once and skip it when it has no lite topics; the same resolved record also\nsupplies lastAccessTime, which lets the separate clientLastAccess lookup go away.\n\nBatch integration of two fixes by the same contributor; #7203 is closed as merged\ninto this one. Verified on the combined branch: 65 backend tests across the\naffected classes plus the full suite, and both changes are mutation-sensitive -\nremoving the post-checkpoint cancellation check fails 2 of the 3 cases in\nCollectorCancellationCheckpointTest, and removing the lite-topic filter fails\nlistLiteTopicsShouldCountOnlyClientsSubscribedToTheParentTopicTest."
    },
    {
      "commit": "c271c1ef6892ffa4789fd213cc12792dd22067e2",
      "tree": "007719dbcef8f5939b20587a9ba96b76c45170ff",
      "parents": [
        "66519d03d21393fcde0cc8298ecce31be2147333"
      ],
      "author": {
        "name": "Qingyuan Wang",
        "email": "146524024+NUAAwqy@users.noreply.github.com",
        "time": "Wed Oct 28 00:41:00 2026 +0800"
      },
      "committer": {
        "name": "GitHub",
        "email": "noreply@github.com",
        "time": "Wed Oct 28 00:41:00 2026 +0800"
      },
      "message": "fix(web): reject deleting a missing alert rule in mock mode (#7122)\n\nMock mode\u0027s deleteAlertRule returned success for a rule that was not there, so the\nmock surface and the real API disagreed about the one case a caller has to handle.\nThrow \"Alert rule not found\" instead, matching the server.\n\nThe test conflicted with #7140, which added semantic-fingerprint de-duplication to\nthe mock createAlertRule; both sets of cases are kept, and the new fixture uses a\ndistinct metric so the de-duplication cannot swallow it."
    },
    {
      "commit": "66519d03d21393fcde0cc8298ecce31be2147333",
      "tree": "bab3026d4dfc853989ab8d902c23ef1ad649dc09",
      "parents": [
        "fe44f3a47dd3fc8110e2617b60cf6dcb996ce784"
      ],
      "author": {
        "name": "pilichoumao",
        "email": "99873428+pilichoumao@users.noreply.github.com",
        "time": "Wed Oct 28 00:38:55 2026 +0800"
      },
      "committer": {
        "name": "GitHub",
        "email": "noreply@github.com",
        "time": "Wed Oct 28 00:38:55 2026 +0800"
      },
      "message": "fix(alert,deploy): fence stale rule evaluations; align upgrade schema (#6312)\n\n* fix(alert): reject stale rule evaluations under a row lock (#6312)\nEvaluation sampled a rule, spent time collecting and aggregating, then wrote\nruntime state, lifecycle events and notification outbox rows against that sample -\nso a rule that was disabled or reconfigured in between still got the stale write.\nBoth the sampled-evaluation and the missing-state reconciliation paths now take a\ncurrent SELECT ... FOR UPDATE read of the rule inside their existing transaction\n(findRuleByIdForUpdate) and skip the write when the rule is gone, disabled, or its\nevaluation identity differs from the snapshot. The lock is held through the writes,\nso a concurrent management update waits and then applies its normal runtime-state\ncleanup. A locking read also observes committed management changes under MySQL\nREPEATABLE READ, which a second ordinary snapshot read would not. The fingerprint\naccessor is renamed of -\u003e evaluationIdentity to match AlertService.\nThe test\u0027s timing assertion was replaced with a positive observation of the lock\nwait (information_schema.innodb_trx under MySQL, INFORMATION_SCHEMA.SESSIONS under\nH2) so it cannot pass vacuously on a slow CI runner, and twelve duplicated\nstubCurrentRules calls were dropped in favour of the shared helper.\n* fix(deploy): align cloud credential upgrade schema (#6210)\ndeploy/mysql/upgrade-cloud-vendor.sql created the credential table with\n`id VARCHAR(64)`, `created_at` / `updated_at` and a `credential_id VARCHAR(64)`\nforeign key, while schema.sql - and the entity, which declares\n`@TableId(type \u003d AUTO) Long id` with gmtCreate / gmtModified - uses a bigint\nauto-increment primary key and the gmt_* columns. A database built by the upgrade\nscript therefore did not match the one built from schema.sql, and\nCloudCredentialSchemaMigration only adds indexes and foreign keys, so nothing\ncorrected the columns afterwards. Align the script with schema.sql column by\ncolumn.\n\nBatch integration of two fixes by the same contributor; #6210 is closed as merged\ninto this one. Verified: 237 backend tests across the affected classes,\nAlertRuleEvaluationConcurrencyTest 6/6 on three consecutive runs, and dropping\nFOR UPDATE from the locking read fails\ndisableShouldWaitForLockedEvaluationAndThenPurgeItsStateTest - which is what shows\nthe rewritten lock-wait assertion is really observing the lock."
    },
    {
      "commit": "fe44f3a47dd3fc8110e2617b60cf6dcb996ce784",
      "tree": "e413820fc7160a5723c227bb16b9d4251b7cc95f",
      "parents": [
        "aaf1d367c58af9d7508ebb28d726c25b79c601fe"
      ],
      "author": {
        "name": "aias00",
        "email": "liuhongyu@apache.org",
        "time": "Wed Oct 28 00:38:24 2026 +0800"
      },
      "committer": {
        "name": "GitHub",
        "email": "noreply@github.com",
        "time": "Wed Oct 28 00:38:24 2026 +0800"
      },
      "message": "fix(message,ai): reject empty payloads early; ignore late stop errors (#7207)\n\n* fix(message): reject empty payloads before resolving clients (#7207)\nAn empty body was only caught on the native path, and then late: after clients had\nbeen resolved, so a cloud redelivery of a stored message whose body came back null\nwent through. Declare @NotEmpty on SendMessageDTO and add an authoritative guard in\nMetadataService.requireSendMessageRequest, the single funnel for both send and\nredelivery ahead of provider dispatch. The guard uses isEmpty() rather than the\nexisting requireName/hasText helpers on purpose - a whitespace-only body such as\n\" \\t\\n\" is a legitimate payload and must reach the broker verbatim instead of being\nrejected or trimmed. The native-path check stays as defence in depth so its FAILED\naudit row is preserved, and docs/api-spec.md 5.8 records the 400.\n* fix(ai): ignore stop errors after the owned run ends (#7000)\nA stop request that failed after the run had already finished surfaced as an error\nand left the composer in the stopping state. Track run ownership, clear\nstopRequested before the early return, and swallow the stop failure once the run is\nover.\n\nBatch integration of two fixes by the same contributor; #7000 is closed as merged\ninto this one. The review note that the AI tool path also needed the empty-body\ncheck did not hold on the current trunk - message.yaml already declares body as\nrequired with minLength 1 and ToolValidationFilter enforces it at runtime - so the\nremaining gap was the cloud redelivery path described above, now covered by\ncloudRedeliveryOfAnEmptySourceBodyIsRejectedTest. Verified: 230 backend tests\nacross the affected classes, 19 frontend tests for useAgentRun, prettier / eslint /\ntsc clean, and removing the guard fails both parameterized cases."
    },
    {
      "commit": "aaf1d367c58af9d7508ebb28d726c25b79c601fe",
      "tree": "eaa4e99c399d90f47cd24adf2e6b97a27d4e13e3",
      "parents": [
        "2df0a70e17335ce597ac213054e46e6d1ad7441f"
      ],
      "author": {
        "name": "Apulupie",
        "email": "167634780+Frun1na@users.noreply.github.com",
        "time": "Tue Oct 27 23:44:04 2026 +0800"
      },
      "committer": {
        "name": "GitHub",
        "email": "noreply@github.com",
        "time": "Tue Oct 27 23:44:04 2026 +0800"
      },
      "message": "fix(studio): report unknown size and delay instead of zero (#5649)\n\n* fix(message): report an unknown cloud message size instead of a fabricated zero (#5649)\nThree call sites rendered a message whose body size the vendor API never reported\nas 0 bytes: Aliyun\u0027s ListMessages returns a nullable Integer BodySize, and\nTencent\u0027s DescribeMessage / MessageItem models have no size getter at all\n(verified against aliyun 5.0.8 and trocket 3.1.1498). An operator reading\n\"0 B\" was told the message was empty. Carry MessageRecordVO.UNKNOWN_SIZE (-1) -\nthe sentinel the repository already uses for QueueProgressVO.UNKNOWN_OFFSET and\nin web/src/utils/consumerLag.ts - and keep it out of every user-facing surface:\nformat.ts gains UNKNOWN_MESSAGE_SIZE and formatOptionalBytes next to the existing\nformatOptionalTimeMs, wired into the message table, the detail drawer and\nQueueBrowser; message.yaml declares minimum: -1 with a description on all three\nsize properties, following group.yaml; and docs/api-spec.md states that -1 means\nthe vendor did not report a size. The AI contract records (MessageItem,\nMessageQueryOutput, RedeliverySource) keep the sentinel in structured output and\ndocument it, the same split #5291 applied to the reset-offset plan: structured\nfields carry the sentinel, prose never does.\n* fix(ai): stop publishing an unmeasurable consume delay as zero seconds (#6106)\ngroup.detail\u0027s delaySeconds was a primitive int, so a group whose delay could not\nbe measured was published to the model as \"0 seconds\" - a fabricated fact rather\nthan an absent one. Make it Integer and omit it when unknown, following\nretryMaxTimes in the same record, which is already an Integer that is null and\nabsent from required. The catalog is regenerated for both this and the message.yaml\nchange above in a single pass; make catalog-verify reports no drift.\n* fix(instance): surface the unique-name race as a duplicate-name error (#5297)\nTwo concurrent creates can both pass requireUniqueInstanceName; the loser hits the\nuk_instance_name unique key and the catch in InstanceService only translated the\ncloud-credential foreign-key violation, rethrowing everything else into a bare 500.\nWalk the cause chain for that key and throw DuplicateInstanceNameException, so the\nraced path answers with the same 400 as the sequential one.\n\nTwo of these needed work to land on the current baseline. #5297\u0027s test fixture\npredates #7353, which made nameserverId mandatory on the APACHE create path, so\nthe fixture now supplies it; the new test method also gained the repository\u0027s Test\nsuffix and the java.sql.SQLException import moved into the java.* group. #5649\nconflicted with #7033 in TencentInstanceProvider (both kept, verified line by\nline) and in MessagePage.test.tsx, where this change and trunk each append a test\nto the same describe block - both are kept.\n\nBatch integration of three related fixes by the same contributor; #5297 and #6106\nare closed as merged into this one. Verified on the combined branch after rebasing\nonto the current trunk: 4161 backend tests, 2019 frontend tests across 166 files,\nthe production build and type check, license gate 8/8, eslint with no new warnings,\nand make catalog-verify / go build / go test / go vet / make fmt all clean."
    },
    {
      "commit": "2df0a70e17335ce597ac213054e46e6d1ad7441f",
      "tree": "77df90a15306ea9eb3882119b7a1047d615238ba",
      "parents": [
        "da2513c9f43e0b26b2540ba2bc56324e879576d0"
      ],
      "author": {
        "name": "烤化の初雪",
        "email": "yangzihao_41@outlook.com",
        "time": "Tue Oct 27 21:38:48 2026 +0800"
      },
      "committer": {
        "name": "GitHub",
        "email": "noreply@github.com",
        "time": "Tue Oct 27 21:38:48 2026 +0800"
      },
      "message": "fix(studio): stop presenting unmeasured values as measured (#5918)\n\n* fix(cluster): stop rendering a failed broker runtime-stats read as 0% and 0 TPS (#5918)\nThe cluster page sorted and rendered disk usage and TPS from whatever the runtime\nstats read returned, so a failed read showed as 0% and 0 TPS - a broker that could\nnot be queried looked like an empty, healthy one. Track statsUnavailable and\nrender \"unavailable\" with direction-aware sorting, the way BrokerCluster.tsx on\nthe studio page already did. The three bare sorters are the ones the page had.\n* fix(consumer): stop presenting an unmeasurable consume delay as 0 seconds (#5920)\ndelaySeconds had no availability signal, so a group whose delay could not be\nmeasured rendered as \"0 seconds\" in the list, the detail drawer, the CSV export\nand the diagnostics summary. Propagate the flag the backend already sends\n(ConsumerGroupVO) through metadata.ts, consumerService.ts and\nconsumerGroupDiagnostics.ts, render \"unavailable\" at all five consumption points,\nand sort unmeasured rows to the bottom in both directions. While landing it we\nadded the ascending-sort assertion the review asked for - without it the\nMAX_SAFE_INTEGER branch of the local delaySortValue had no coverage - and fixed a\nline-wrap in the PR\u0027s own consumerService.test.ts that failed prettier.\n* fix(consumer): keep unmeasured rows out of the top of a descending sort (#5902)\nThe total-lag sorter treated an unmeasured value as the largest possible number,\nso a descending sort put the rows we know least about at the top. Add a sortOrder\nso unmeasured rows sink in both directions, following compareResourceCounts on the\ninstance page and unmeasuredSortKey on the LiteTopic page. Scope note: this covers\nthe total-lag column only. The PR as submitted also touched consumerConnections.ts,\nbut trunk has since replaced onlineInstancesSortValue with onlineInstancesCount\n(#6916, where an unavailable count renders as 0 by design), and the author\u0027s own\nmerge of trunk had already dropped that part - so it is not included here.\n* fix(acl): apply the rule version filter the console sends (#5900)\nThe console has always sent aclVersion (all / 1.0 / 2.0) and the repository\npredicate already existed, but the controller never declared the parameter and the\nservice hard-coded null, so the version dropdown was a dead control. Thread it\nthrough the controller and the service and add the version comparison to the\nTencent in-memory filter chain. The 7-argument listRules overload stays: \nAclListToolHandler still calls it.\n* fix(alert): stop the notification template from rendering values in scientific notation (#5889)\nThresholds and values went through String.valueOf, so a large threshold reached\non-call as \"1.0E+7\". Route both render points through formatNumber.\n* fix(auth): stamp the user and session audit columns in UTC (#5886)\nThe user and session tables left gmt_create / gmt_modified to database defaults,\nso the recorded instant depended on the server\u0027s timezone while the rest of the\nauth path is UTC - and #7311 had started surfacing gmt_create to the UI through\nStudioUserSessionDetailVO and StudioUserVO. Write both columns explicitly from\nAuthService.now(). Landing this needed the write points added since the PR was\nopened (#7311 and #7312) to be covered too, so all 13 write points now stamp\nexplicitly rather than half the table being UTC and half the database default;\nthe conflicts in AuthService were resolved as a union, keeping #7312\u0027s\npasswordMustChange and #7230\u0027s monotonic last_seen_at guard.\n\nBatch integration of six related fixes by the same contributor; #5886, #5889,\n#5900, #5902 and #5920 are closed as merged into this one. Verified on the\ncombined branch: 4157 backend tests, 2016 frontend tests across 166 files, the\nproduction build and type check, license gate 8/8, and eslint with no new\nwarnings against the trunk baseline."
    },
    {
      "commit": "da2513c9f43e0b26b2540ba2bc56324e879576d0",
      "tree": "4dd4182f49aec589eb5ce6ce612d23967e3bbfaa",
      "parents": [
        "706f508917db3312884ebfc46c78495dc0f1a501"
      ],
      "author": {
        "name": "bai mengxuan",
        "email": "2013373049@qq.com",
        "time": "Tue Oct 27 20:06:09 2026 +0800"
      },
      "committer": {
        "name": "GitHub",
        "email": "noreply@github.com",
        "time": "Tue Oct 27 20:06:09 2026 +0800"
      },
      "message": "fix(studio): scope ACL pushes, gate settings writes, fix unmeasured values (#6851)\n\n* fix(acl): scope the broker push to the instance\u0027s cluster (#6851)\nThe ACL push resolved every master behind the instance\u0027s NameServer, so on a\nNameServer shared by several clusters one instance\u0027s accounts and policies were\nwritten to brokers it is not bound to. masterAddresses() now takes the instance\nand filters through AclBrokerScope - the same scope the ACL read path already\nused - and an instance that names no cluster keeps every master, as before.\n* fix(settings): prefer instance names in data source labels (#6267)\nformatInstanceIds did a single find and OR-ed the results, so a data source\nreferencing both a name and a numeric key rendered the key. Look names up first\nand fall back to the id, which also fixes the exported CSV.\n* fix(web): keep a reader out of the AI settings writes (#6865)\nThe AI, data source and cloud credential tabs had no role gate, so a reader\naccount reached the write handlers. Derive canWriteSettings once in a shared\nuseCanWriteSettings hook - the same expression GeneralSettingsTab already used -\ndisable the write buttons and modal confirmations, and return early in the\nhandlers. The gate is checked before the submitting lock so a rejected attempt\ncannot leave the modal permanently locked.\n* fix(clients): ignore source port changes in ID collision diagnostics (#6203)\nThe client-id collision check compared full addresses, so a reconnect that only\nchanged the TCP source port was reported as a critical collision. Compare hosts\nvia the new connectionHost() helper.\n* fix(cluster): count every listed connection in the client summary (#6321)\nThe summary tiles de-duplicated by type:clientId while the table, subtitle and\nfooter counted rows, so the numbers disagreed. All five figures now come from\nthe listed connection rows.\n* fix(alert): invalidate test results when criteria change (#6297)\nA dry-run result stayed on screen after the rule criteria changed, so an operator\ncould act on a stale verdict. Track the 13 criteria fields, invalidate on change,\nand add a generation guard to handleTest so an in-flight response cannot land on\na newer edit. Every clear path now goes through invalidateTestResult(), which\nalso bumps the generation.\n* fix(web): tolerate Git line endings in the license gate (#6189)\nWith core.autocrlf the vendored-icon comparison and the distributed LICENSE were\nbyte-compared against LF-normalized manifests, so a Windows checkout failed the\ngate. Normalize both sides through eolInsensitive() and cover it with a CRLF\nfixture that exercises the vendored SVG branch.\n* fix(consumer): clear selection after confirmed empty refresh (#6352)\nA refresh that confirmed the list is empty left the previous selection in place,\nso a following bulk action targeted rows that no longer exist.\n* fix(instance): stamp the ownership reservation\u0027s bookkeeping columns (#6668)\nThe two reservation INSERTs relied on database defaults for gmt_create and\ngmt_modified, which left them null where no default applied and let the two\ncolumns disagree. Write both explicitly from one LocalDateTime.now().\n* fix(tencent): derive a topic consumer\u0027s subscription model from MessageModel (#7033)\nconsumeType was read from the wrong field, inverting the clustered/broadcast\nverdict for Tencent topic consumers. The parameter is renamed to messageModel so\nthe signature does not contradict what it receives.\n* fix(rmqctl): declare and enforce the paged tools\u0027 page size ceiling (#6845)\nThree paged tools documented pageSize with only a minimum, so the CLI accepted\nvalues the services reject with 400 (all three cap at 100). Declare maximum: 100\nin the catalog and carry it end to end; catalog_gen.go is regenerated and\n`make catalog-verify` reports no drift. The constraint existed in rmq-tools.yaml\nbefore #4225 split the catalog and was dropped there, because the generator\u0027s\nallow-list did not include `maximum`.\n\nBatch integration of eleven related fixes by the same contributor; #6189, #6203,\n#6267, #6297, #6321, #6352, #6668, #6845, #6865 and #7033 are closed as merged\ninto this one. Verified on the combined branch: 4143 backend tests, 2009 frontend\ntests across 166 files, license gate 8/8, production build, eslint with no new\nwarnings, and catalog-verify / go test / go vet / make fmt all clean."
    },
    {
      "commit": "706f508917db3312884ebfc46c78495dc0f1a501",
      "tree": "8986f7c79f1ac78b505c71dd5973ff2e1e02828c",
      "parents": [
        "aa377cf4196e8cc8a96b2ce7d412c0647df37d6a"
      ],
      "author": {
        "name": "zmuxuny",
        "email": "xiahoumuxuan@gmail.com",
        "time": "Tue Oct 27 03:27:40 2026 -0700"
      },
      "committer": {
        "name": "GitHub",
        "email": "noreply@github.com",
        "time": "Tue Oct 27 18:27:40 2026 +0800"
      },
      "message": "fix(ai): isolate persisted conversations in Mock mode (#7128)\n\nMock mode is a demo/offline surface, but it still loaded and mutated persisted AI\nconversations: a deep link to /ai/c/\u003cid\u003e attached a real conversation, the history\ndrawer mounted its live query and delete actions, and a home-page draft was\nauto-sent, creating a row in the real store.\n\nGate all three on the mode. The history modal takes a disabled prop and renders\nan InfoBanner explaining that saved conversations are unavailable in mock mode\ninstead of mounting the panel, so no query or action is reachable; the draft\nhandoff takes sendEnabled and consumes the draft locally without creating a\nconversation or sending it; and the deep link is not attached. Adds the\nai.mockConversationsUnavailable key in zh and en."
    },
    {
      "commit": "aa377cf4196e8cc8a96b2ce7d412c0647df37d6a",
      "tree": "881601c64fd0f84af0550064ca533c8dfe39d01b",
      "parents": [
        "d6ff5e4fc2fe9693c3acbd74d97c918e8efae596"
      ],
      "author": {
        "name": "zmuxuny",
        "email": "xiahoumuxuan@gmail.com",
        "time": "Tue Oct 27 03:26:18 2026 -0700"
      },
      "committer": {
        "name": "GitHub",
        "email": "noreply@github.com",
        "time": "Tue Oct 27 18:26:18 2026 +0800"
      },
      "message": "fix(consumer): require loaded settings before saving (#6764)\n\nThe consumer group settings drawer let an operator press Save before the initial\nread had landed, so the form submitted its empty defaults over the group\u0027s real\nconfiguration - a write that bypassed the confirmation the loaded values are\nsupposed to give.\n\nTrack the initial read with settingsReady / settingsFailed: the Save and Reset\nbuttons stay disabled until it succeeds, saveSettings() returns early unless the\nstate is ready and not loading, a failed read renders an error with a retry that\nre-arms the form, and closing the drawer clears both flags plus the captured\noriginal values so the next group cannot inherit them. The hardcoded Chinese\nfailure toast moves to the consumer.settingsLoadFailed i18n key.\n\nThe tests pin the button-level disabling; the early return in saveSettings() is\ndefence in depth and is not covered by an assertion of its own."
    },
    {
      "commit": "d6ff5e4fc2fe9693c3acbd74d97c918e8efae596",
      "tree": "f1424b5e881ff620102a767f3f5eb244c7c1cb25",
      "parents": [
        "9c270f50bbbe160309338b86bdd568ece2f5bb4f"
      ],
      "author": {
        "name": "Apulupie",
        "email": "167634780+Frun1na@users.noreply.github.com",
        "time": "Tue Oct 27 16:37:40 2026 +0800"
      },
      "committer": {
        "name": "GitHub",
        "email": "noreply@github.com",
        "time": "Tue Oct 27 16:37:40 2026 +0800"
      },
      "message": "docs(api-spec): document the previously undocumented endpoints (#4835)\n\nThe endpoint quick-reference covered 126 of the 234 mappings in the codebase and\nwhole controllers had no section at all. This adds the missing ones and takes\ncoverage to 190 of 234.\n\nThe change is applied as an insertion into the current document rather than as\nthe originally submitted rewrite: every line of the pre-existing spec is\npreserved in order, with two exceptions - the chapter 17 preamble, which said\nthe `/api/studio-users` list and create surface was \"not yet documented\" and is\ncontradicted by the 17.7-17.12 sections added here, so it now describes what the\nchapter actually covers.\n\nStructure: chapter 3 (Instances) fills the numbering gap the document has had\nsince it was written, chapters 18-21 (Cloud Credentials, LLM configuration, Ops\nconfiguration, Cloud catalog) are appended, and the remaining additions are\nappended to the end of the chapter they belong to (4.21-4.28, 15.15, 17.7-17.12\nand others), so no existing section number moves. Top-level chapters now run\n1-21 with no gaps and the 190 subsections have no duplicate or skipped numbers.\n\nEvery documented path was cross-checked against the controller mappings,\nincluding the array form (`@RequestMapping({\"/api/alert-rules\",\n\"/api/business-alert-rules\"})`), and every request/response field against the\nDTO or VO - `@RequestParam` defaults and required flags, `@Valid` constraints,\n`PageResult` shapes and the `Result\u003cT\u003e` wrapper. That caught and corrected a\nnumber of claims in the original submission, among them an endpoint documented\nas returning an empty array where the controller returns 404, a query documented\nwithout three of its parameters, a path that does not exist, and an\n`InstanceVendor` list missing `APACHE`.\n\nKnown gaps left as-is, all pre-existing and out of scope here: §4.10 and §7.6\nhave prose sections but no quick-reference row; §4.10 documents `clusterId` as\nrequired while the controller has it optional and does not mention `instanceId`;\n§16.1 documents `metric` as required while `MetricQueryDTO` accepts `metric` or\n`profileId` + `semanticMetric`; §12.1\u0027s `SystemAlert` table is a reduced view of\n`SystemAlertVO`; and the `/api/business-alert-rules` alias is only mentioned at\nchapter level. 44 mappings remain undocumented, most of them aliases of\nendpoints that are documented under their other path."
    },
    {
      "commit": "9c270f50bbbe160309338b86bdd568ece2f5bb4f",
      "tree": "6d4bf6adedacb13732386bfbd1247715b5087fba",
      "parents": [
        "e457080569850efb7d7de374bbaa30a24701536f"
      ],
      "author": {
        "name": "wizcraft_kris",
        "email": "99409434+Kris20030907@users.noreply.github.com",
        "time": "Tue Oct 27 16:15:31 2026 +0800"
      },
      "committer": {
        "name": "GitHub",
        "email": "noreply@github.com",
        "time": "Tue Oct 27 16:15:31 2026 +0800"
      },
      "message": "feat(litetopic): create Lite parent topics with an explicit TTL (#6644)\n\nAdds the create half of LiteTopic parent-topic lifecycle management.\n\nA Lite parent topic has to carry `+message.type\u003dLITE` and `+lite.topic.expiration`\nin the same write, or the broker ends up with a parent that never expires. This\ngoes through `MetadataService.createTopic`, so ownership, audit and catalog\nbehaviour match every other topic create, and the delete half is left for a\nfollow-up.\n\nThree points were fixed while landing it:\n\n1. Instance-level capability gate. `guardLiteCreate` now runs after\n   `requireWriteInstance` (so it sees the canonical instance name) and calls\n   `ownershipGuard.requireInstance` + `requireSupportedProvider`, the same pair\n   `extendTTL` uses. Without it a direct API call with `type\u003dLITE` could create a\n   Lite parent on a cloud-vendor instance: `AliyunInstanceProvider.createTopic`\n   forwards only `messageType` and has no TTL field, so the topic would exist\n   outside the vendor control plane with no expiry. Cloud vendors now get 501.\n\n2. An omitted TTL is filled with `DEFAULT_LITE_TTL_MINUTES` (1440) instead of\n   being forwarded as absent. In rocketmq-common 5.5.0,\n   `TopicAttributes.LITE_EXPIRATION_ATTRIBUTE` is\n   `LongRangeAttribute(\"lite.topic.expiration\", true, -1, DAYS.toMinutes(30), -1)`\n   - min -1, max 43200 minutes, default -1 - and a non-positive value means the\n   topic never expires. The AI tool `rmq.topic.update` can also create a topic\n   and its input schema has no TTL field, so without a server-side default that\n   path could create a never-expiring parent. This mirrors how `writeQueues` /\n   `readQueues` already fall back to 8.\n\n3. Unit divergence documented rather than papered over. The native unit of\n   `lite.topic.expiration` is minutes, so create takes minutes; `extendTTL`\n   takes milliseconds. Converting create to milliseconds would lose precision\n   (round(ms/60000)) and 30 days does not fit an int, so both units are kept and\n   stated together in `docs/api-spec.md` 5.3 / 5.4 / 5.11, in the DTO and VO\n   javadoc, and in the front-end field hint, with the conversion bound\n   `minutes \u003d clamp(round(millis / 60000), 1, 43200)`.\n\nContract sync: `TopicVO` exposes `liteTtlMinutes` with `@JsonInclude(NON_NULL)`\nso it is absent for non-Lite topics, and `UpdateTopicDTO` binds the field so the\ndocumented \"400 when liteTtlMinutes is sent with a non-LITE type\" is actually\nenforced instead of silently dropped.\n\nScope note: `liteTopicProvider.isSupported()` probes the nameserver address from\nStudio\u0027s own configuration, not the target instance\u0027s, so the 501 gate is\ncluster-global - the same semantics the existing `GET /api/liteTopic/capability`\nendpoint already has. In a multi-instance deployment where the default endpoint\nand the target instance are different clusters, the gate can be wrong in either\ndirection. `docs/api-spec.md` 5.3 states this scope explicitly; making the probe\ninstance-aware needs `LiteTopicProvider.isSupported(instanceId)` plus a resolver,\nwhich touches the capability endpoint and its contract, so it is left to a\nfollow-up."
    },
    {
      "commit": "e457080569850efb7d7de374bbaa30a24701536f",
      "tree": "6e8957f05e97bd8534ab53dc50c7627f5f3085ca",
      "parents": [
        "ca348bdb6755a71083bc57203fcd2380c188d245"
      ],
      "author": {
        "name": "Apulupie",
        "email": "167634780+Frun1na@users.noreply.github.com",
        "time": "Tue Oct 27 15:46:50 2026 +0800"
      },
      "committer": {
        "name": "GitHub",
        "email": "noreply@github.com",
        "time": "Tue Oct 27 15:46:50 2026 +0800"
      },
      "message": "fix(group): preview offset resets for offline consumer groups (#5291)\n\n* fix(group): preview the offset reset from the topic route for stopped groups\n\nThe preview called examineConsumeStats and, on CONSUMER_NOT_ONLINE, returned an\nempty preview with allowReset\u003dfalse - so a stopped group could never be previewed\neven though POST /api/groups/reset-offset (which does not validate a preview)\nreset it regardless. In rocketmq-tools 5.5.0, resetOffsetNew calls\nresetOffsetByTimestamp(force\u003dtrue) and, on response code 206, falls back to\nresetOffsetByTimestampOld(force\u003dtrue), which reads the broker offset store and\nwrites searchOffset() for each queue of the topic route. The preview now walks\nthat same route and issues the same per-queue search, so an offline group sees\nthe targets the reset will actually commit.\n\nBroadcast groups (213) and groups that never consumed (17 on %RETRY%\u003cgroup\u003e) are\nstill blocked up front and keep returning the empty, non-resettable preview.\n\n* fix(group): keep the offline reset preview free of the -1 sentinel\n\nCurrent offsets genuinely cannot be read for a group that is not connected, so the\nlag and delta fields carry the same unknown sentinels the online path already\nuses. They must not reach user-facing prose: the tool plan read \"projected lag\nchanges from -1 to 500\", and the Chinese description column echoed an English\nsentence built around a delta. Both now render an explicit unavailable state, and\nthe aggregate net-delta field renders \u0027-\u0027 when any queue had no current offset,\nmatching what the per-queue column already did.\n\nDisclosure - this unblocks two gates derived from the preview. rmq.group.reset_offset\nis riskLevel L2 with permission group:write and requires a confirm_token that only a\nsuccessful preview() can mint, and the UI\u0027s confirm-reset button keys off the same\nallowReset, so an offline group could not confirm a reset through either path before.\nNo new capability is granted: the REST endpoint never validated a preview, and the\nserver-side execution path has used resetOffsetNew since #3310. The gates themselves\nare unchanged - !complete || !allowReset still throws 409, and any queue that fails\nto preview still marks the whole preview incomplete. The plan gains a warning that\ncurrent offsets are unavailable so deltas cannot be computed.\n\nKnown residual case, not introduced here: a broadcast group that is also offline\nraises 206 before the broadcast check can run, so it reaches the offline preview and\nis let through. resetOffsetNew then writes broker-side offsets that broadcast\nconsumers never read - identical to `mqadmin resetOffsetByTime` on the same input,\nand already reachable through the REST endpoint. The subscription mode lives on the\nclient while a group is offline, so the server cannot rule this out; the warning\nhands the uncertainty to whoever confirms the reset."
    },
    {
      "commit": "ca348bdb6755a71083bc57203fcd2380c188d245",
      "tree": "d70d1245bf54cace8ac30bcb2d2792b6d3c70e58",
      "parents": [
        "184cd00f95fe9e556da8a5770e0f8c238f5b561a"
      ],
      "author": {
        "name": "bai mengxuan",
        "email": "2013373049@qq.com",
        "time": "Tue Oct 27 14:22:00 2026 +0800"
      },
      "committer": {
        "name": "GitHub",
        "email": "noreply@github.com",
        "time": "Tue Oct 27 14:22:00 2026 +0800"
      },
      "message": "fix(ai): preserve handoff models when the catalog loads (#6274)\n\nThe model chosen on the home page was merged into the same state that the\ncatalog load replaces, so a catalog response arriving after the handoff\nsilently dropped it: the AI menu no longer offered the model the composer\nsaid was selected.\n\nKeep handoff-added options in their own state and expose the union, with the\ncatalog entry winning on duplicates, so a late catalog load can no longer\ndiscard them. Disabling the runtime clears both."
    },
    {
      "commit": "184cd00f95fe9e556da8a5770e0f8c238f5b561a",
      "tree": "738ee95d2e6711471d68bcca02d0a1897de02833",
      "parents": [
        "8aebce7fc1f4e17388c4e446f6e184c57f7625ec"
      ],
      "author": {
        "name": "Wang1rrr",
        "email": "153259368+Wang1rrr@users.noreply.github.com",
        "time": "Tue Oct 27 12:05:19 2026 +0800"
      },
      "committer": {
        "name": "GitHub",
        "email": "noreply@github.com",
        "time": "Tue Oct 27 12:05:19 2026 +0800"
      },
      "message": "fix(cluster,metrics): correct IPv6, PEM, query URI and dashboard defects (#7009)\n\n* fix(metrics): preserve data source URI paths and query parameters (#7009)\nThe metrics data source address was joined to the query path by string\nconcatenation after stripping a trailing slash, so a base URL carrying a path\nprefix or a query string produced a wrong endpoint and re-encoded characters\nthat were already encoded. PrometheusQueryUri now appends path segments through\nURI components and builds the final URI once.\n* fix(metrics): encode Basic authentication credentials as UTF-8 (#7011)\nSpring\u0027s two-argument setBasicAuth encodes as ISO-8859-1: it throws for CJK or\nemoji credentials and silently produces the wrong bytes for accented Latin-1.\nPass StandardCharsets.UTF_8 explicitly at both call sites.\n* fix(nameserver): reject IPv6 network prefixes in endpoints (#6970)\nAn IPv6 literal carrying a CIDR prefix length parsed as a nameserver address and\nwas later handed to the client, which cannot connect to a prefix. Treat a `/`\ninside an IPv6 literal as invalid.\n* fix(message): normalize IPv6 broker topology endpoints (#6975)\nThe known-broker-endpoint guard compared hosts textually, so in an IPv6 cluster\na legitimate message id was rejected as referring to an unknown broker.\nNormalize a host that contains a colon through InetAddress before adding it to\nthe set - no DNS resolution is introduced.\n* fix(instance): cancel queued resource counts on shutdown (#6978)\nclose() shut the executor down but left the queued futures alone, so shutdown\nblocked for the full 3s await and then reported a state that never happened.\nCancel the futures returned by shutdownNow().\n* fix(k8s): parse PEM bundles without joining Base64 blocks (#6989)\nparseCertificate stripped the PEM armour, concatenated the Base64 of every\ncertificate in the bundle and rewrapped it, which corrupts any bundle holding\nmore than one certificate. Hand the PEM text to the X.509 reader as-is.\n* fix(grafana): query native broker metrics by node ID (#7004)\nThe native broker metrics only carry a node_id label, so the $broker variable\nand the three native TPS panels matched nothing. Query them by node_id and\nregenerate the twelve dashboards from gen_grafana_dashboards.py.\n\nBatch integration of seven related fixes by the same contributor; #6970, #6975,\n#6978, #6989, #7004 and #7011 are closed as merged into this one."
    },
    {
      "commit": "8aebce7fc1f4e17388c4e446f6e184c57f7625ec",
      "tree": "ea6c08ba48c4463a31592a420dcb78a875bf7976",
      "parents": [
        "1053cd4be782210833c35c914d136de7a29c95c6"
      ],
      "author": {
        "name": "Wang1rrr",
        "email": "153259368+Wang1rrr@users.noreply.github.com",
        "time": "Tue Oct 27 12:04:51 2026 +0800"
      },
      "committer": {
        "name": "GitHub",
        "email": "noreply@github.com",
        "time": "Tue Oct 27 12:04:51 2026 +0800"
      },
      "message": "fix(build): correct Docker, packaging and deploy-script defects (#6991)\n\n* fix(build): select the Node distribution for the target architecture (#6991)\nThe image hard-coded the x64 Node tarball and five x64 symlinks, so an arm64\nbuild produced an image whose node/npm/npx/claude/qodercli links pointed at\nbinaries that cannot run. Pick the distribution from TARGETARCH (x64 or arm64)\nand fail the build on an unknown architecture instead of installing a broken\ntoolchain.\n* fix(build): copy legal inputs into the Maven Docker stage (#6985)\nThe Maven stage ran the license check without legal-determinations.json and\nlegal/licenses, so a Docker-only build could not reproduce the check that the\nhost build performs. Copy both inputs before mvn package.\n* fix(build): preserve JAR permissions during legal packaging (#7007)\nThe legal packaging step replaced each JAR through a temporary file created\nwith the default 0600 mode, silently dropping the executable and group bits of\nthe original artifact. Copy the source mode onto the replacement before the\natomic rename.\n* fix(deploy): quote remote installation paths (#6973)\nEvery remote command interpolated $REMOTE_PATH unquoted, so an installation\npath containing a space or a shell metacharacter broke the deployment - and\nwith the pre-OpenSSH-9 scp protocol the remote shell parses the target a second\ntime. Add quote_remote_arg and route every remote use of the path - the ssh\ncommand strings, the scp target and the docker bind mount - through it.\n* docs: preserve the working directory in test commands (#6961)\nThe CONTRIBUTING command blocks used bare `cd`, which leaves the reader in a\nsubdirectory for every following command. Wrap each block in a subshell and say\nthat the commands run from the repository root; the two blocks outside the\nTests section are covered by the same change.\n\nBatch integration of five related fixes by the same contributor; #6961, #6973,\n#6985 and #7007 are closed as merged into this one."
    },
    {
      "commit": "1053cd4be782210833c35c914d136de7a29c95c6",
      "tree": "2f2e506fab036a3dc293a87e30f2348b834f3345",
      "parents": [
        "f6a8aaa87b2b83c20ea3642584344d9d194d1850"
      ],
      "author": {
        "name": "Wang1rrr",
        "email": "153259368+Wang1rrr@users.noreply.github.com",
        "time": "Tue Oct 27 12:04:27 2026 +0800"
      },
      "committer": {
        "name": "GitHub",
        "email": "noreply@github.com",
        "time": "Tue Oct 27 12:04:27 2026 +0800"
      },
      "message": "fix(ai): harden LLM streaming, endpoint resolution and agent runtime (#6998)\n\n* fix(ai): preserve query parameters when resolving API endpoints (#6998)\nBoth apiBase normalizations rebuilt the endpoint by string concatenation, so a\nconfigured base carrying a query string or a fragment produced a malformed URL.\nLlmApiBase now resolves the base through URI components and inserts the path\nahead of the query and fragment; LlmConfigService and OpenAiCompatibleLlmClient\nshare it instead of keeping two private copies.\n* fix(ai): share the stream timeout budget with header reads (#6983)\nThe SSE reader task was granted a fresh requestTimeout even though waiting for\nthe response headers had already consumed part of that same budget, so a slow\nprovider could hold one call for nearly twice the configured timeout. The\nreader now gets the remaining budget.\n* fix(ai): preserve the first event in BOM-prefixed SSE streams (#6994)\nSSE permits a single UTF-8 BOM at the start of the stream, before any field\nname. Strip one leading U+FEFF from the first line only and leave interior\nBOMs alone, so the first event is no longer misparsed.\n* fix(ai): cancel rejected stream response bodies (#6967)\nassertEventStream rethrew without releasing the response body, leaking the\nconnection on every rejected stream. Cancel the body first, then rethrow the\noriginal error.\n* fix(ai): bound partial SSE frames after draining complete events (#6976)\nThe frame size limit was applied right after the read and before complete\nevents were drained, so a large but well-formed chunk was reported as an\noversized remainder. Only the unfinished trailing frame is bounded now.\n* fix(ai): serialize capability probe cache refreshes (#6971)\nConcurrent callers each started their own agent capability probe and all of\nthem waited the full 15s. binaries() now refreshes under a lock and reuses the\nresult of the probe already in flight.\n* fix(ai): resolve workspace paths before changing child cwd (#6980)\nA relative configured path was resolved against the child process working\ndirectory, which the caller had already changed. workspaceRoot() now makes the\npath absolute and normalized before it is handed down.\n* fix(ai): preserve Unicode characters in terminal run errors (#7002)\nTruncating the terminal error fields by char could split a surrogate pair and\nleave the run unpersistable. Use TextBounds.truncate, which respects code point\nboundaries.\n\nBatch integration of eight related fixes by the same contributor; #6967,\n#6971, #6976, #6980, #6983, #6994 and #7002 are closed as merged into this one."
    },
    {
      "commit": "f6a8aaa87b2b83c20ea3642584344d9d194d1850",
      "tree": "5dc48a00adc5f8aa986d0cbfcc70e3c50763e9ba",
      "parents": [
        "740cd73cd4be166529b13a4ebe80006abed7d807"
      ],
      "author": {
        "name": "Loyal-Young",
        "email": "hjy1713252343@gmail.com",
        "time": "Mon Oct 26 23:22:21 2026 +0800"
      },
      "committer": {
        "name": "GitHub",
        "email": "noreply@github.com",
        "time": "Mon Oct 26 23:22:21 2026 +0800"
      },
      "message": "fix(consumer): scope mock progress to its instance (#7253)\n\n* fix(consumer): scope mock progress to its instance"
    },
    {
      "commit": "740cd73cd4be166529b13a4ebe80006abed7d807",
      "tree": "bc362f3322bbfc00e8b271ace339edc01e1b5be3",
      "parents": [
        "09def4364965c47c7d4cfb0231a7e89dec579c82"
      ],
      "author": {
        "name": "Loyal-Young",
        "email": "hjy1713252343@gmail.com",
        "time": "Mon Oct 26 21:23:31 2026 +0800"
      },
      "committer": {
        "name": "GitHub",
        "email": "noreply@github.com",
        "time": "Mon Oct 26 21:23:31 2026 +0800"
      },
      "message": "fix(consumer): order group progress by topic and queue (#7251)\n\n* fix(consumer): order group progress by topic and queue"
    },
    {
      "commit": "09def4364965c47c7d4cfb0231a7e89dec579c82",
      "tree": "4e535bf19ae14176423b1c9455b4af832ff0a14e",
      "parents": [
        "a48d4cbf7dd54dd1c382c9632804296867da2522"
      ],
      "author": {
        "name": "Loyal-Young",
        "email": "hjy1713252343@gmail.com",
        "time": "Mon Oct 26 21:23:08 2026 +0800"
      },
      "committer": {
        "name": "GitHub",
        "email": "noreply@github.com",
        "time": "Mon Oct 26 21:23:08 2026 +0800"
      },
      "message": "fix(consumer): handle large queue health inventories (#7245)\n\n* fix(consumer): handle large queue health inventories"
    },
    {
      "commit": "a48d4cbf7dd54dd1c382c9632804296867da2522",
      "tree": "4894c45a34f50fc64ae8110ff442197547b52bf5",
      "parents": [
        "64c412bc7f87036f7815a09cd277eb986d07ba96"
      ],
      "author": {
        "name": "Loyal-Young",
        "email": "hjy1713252343@gmail.com",
        "time": "Mon Oct 26 21:22:45 2026 +0800"
      },
      "committer": {
        "name": "GitHub",
        "email": "noreply@github.com",
        "time": "Mon Oct 26 21:22:45 2026 +0800"
      },
      "message": "fix(dashboard): classify traffic with unrounded metrics (#7241)\n\n* fix(dashboard): classify traffic with unrounded metrics"
    },
    {
      "commit": "64c412bc7f87036f7815a09cd277eb986d07ba96",
      "tree": "6b3ea1c094d46b0ebd87f30663c9ce4e1f59adbe",
      "parents": [
        "242bfe6e17116172aea3d533edd29de35462b83a"
      ],
      "author": {
        "name": "Loyal-Young",
        "email": "hjy1713252343@gmail.com",
        "time": "Mon Oct 26 21:22:23 2026 +0800"
      },
      "committer": {
        "name": "GitHub",
        "email": "noreply@github.com",
        "time": "Mon Oct 26 21:22:23 2026 +0800"
      },
      "message": "fix(instance): reject duplicate mock instance names (#7237)\n\n* fix(instance): reject duplicate mock instance names"
    },
    {
      "commit": "242bfe6e17116172aea3d533edd29de35462b83a",
      "tree": "859889d0a0fe1fe79e281eca9f2fc39c33823651",
      "parents": [
        "1fc4db92f9bc88c289ef11a150186f94edbb8d70"
      ],
      "author": {
        "name": "Loyal-Young",
        "email": "hjy1713252343@gmail.com",
        "time": "Mon Oct 26 21:21:59 2026 +0800"
      },
      "committer": {
        "name": "GitHub",
        "email": "noreply@github.com",
        "time": "Mon Oct 26 21:21:59 2026 +0800"
      },
      "message": "fix(rmqctl): restore shell state after Windows build (#7060)\n\n* fix(rmqctl): restore shell state after Windows build"
    },
    {
      "commit": "1fc4db92f9bc88c289ef11a150186f94edbb8d70",
      "tree": "2197fbfd7b6758eddfd87b14a4611e25bba06bec",
      "parents": [
        "128fd3c49ce697806be5f40544774df03d08086b"
      ],
      "author": {
        "name": "Loyal-Young",
        "email": "hjy1713252343@gmail.com",
        "time": "Mon Oct 26 21:21:11 2026 +0800"
      },
      "committer": {
        "name": "GitHub",
        "email": "noreply@github.com",
        "time": "Mon Oct 26 21:21:11 2026 +0800"
      },
      "message": "fix(server): match literal search text in the audit, metadata and alert queries (#7086)\n\nIntegration batch of three same-family fixes by Loyal-Young: free-text search in the audit repository (#7074), topic/group name search in the metadata provider (#7083) and rule search in the alert repository (#7086) all passed the raw term into a LIKE condition, so a \u0027%\u0027 or \u0027_\u0027 in the term acted as a wildcard. Each escape is now one shared util (common/util/LikePatterns), which also absorbs the fifth private copy in the AI conversation repository and the one in QueryHistoryService. The audit half keeps the operator equality filter that landed in #7308; the metadata half keeps the subscription-mode filter that landed in #7310. #7074 and #7083 are closed as folded into this carrier; all commits keep their original author."
    },
    {
      "commit": "128fd3c49ce697806be5f40544774df03d08086b",
      "tree": "d2617652737e4fa46970d0bcffbaa5d76ac90780",
      "parents": [
        "9b02adb4ddc6bb87c45bca8d1fa4e07aaa5e19b8"
      ],
      "author": {
        "name": "btlqql",
        "email": "2977859784@qq.com",
        "time": "Mon Oct 26 21:07:37 2026 +0800"
      },
      "committer": {
        "name": "GitHub",
        "email": "noreply@github.com",
        "time": "Mon Oct 26 21:07:37 2026 +0800"
      },
      "message": "fix(web): keep the metrics explorer query history per account (#7145)\n\n* fix(web): keep the metrics explorer query history per account (#5982)\nThe metrics explorer kept its query history in localStorage under one global key\n(`rocketmq-studio-metrics-query-history`), loaded once when the panel mounted and cleared\nonly by the explicit button. Logout clears the auth store\u0027s own keys only, so the next\naccount on a shared browser saw the previous account\u0027s history - PromQL text, profile and\nmetric names, instance ids, data-source keys and the queried windows - in the drawer, and\ncould re-run it. `/ops/dashboard` has no admin gate, so any signed-in account reaches it.\nKey the history by account instead, following the sibling per-account stores\n(`messageTraceTopicStorage`): `metricsQueryHistoryStorageKey(owner)` builds\n`...:user-id:\u003cid\u003e` from the session\u0027s numeric id, falls back to `...:username:\u003cname\u003e` and\nreturns the legacy global key only when no account is signed in (a test, or the first paint\nbefore the session is restored), so an unauthenticated panel never invents a bucket.\n`loadMetricsQueryHistory`/`saveMetricsQueryHistory`/`clearMetricsQueryHistory` now take\nthe owner and the panel passes the one it reads from the auth store, memoized so the query\ncallbacks keep a stable dependency.\nExisting history under the old global key is intentionally not migrated: its owner is\nunknown, and migrating it would hand it to whoever logs in next - the defect itself.\n* test(web): cover the account-scoped query history at the component level\nThe util tests pin the storage key per owner, but nothing exercised the wiring:\nthat MetricsExplorer actually asks for the signed-in account\u0027s key. Seed one entry\nper account and require the history drawer to show the signed-in account\u0027s entry\nand not the other account\u0027s."
    },
    {
      "commit": "9b02adb4ddc6bb87c45bca8d1fa4e07aaa5e19b8",
      "tree": "7379a36389111b66f926de047f67f0d1d1be9775",
      "parents": [
        "2a6810b99c62f0863f34f1592dc29d68ca0ad3c1"
      ],
      "author": {
        "name": "btlqql",
        "email": "2977859784@qq.com",
        "time": "Mon Oct 26 20:52:43 2026 +0800"
      },
      "committer": {
        "name": "GitHub",
        "email": "noreply@github.com",
        "time": "Mon Oct 26 20:52:43 2026 +0800"
      },
      "message": "fix(audit): disclose a cleanup that stopped at its per-call ceiling (#7148)\n\n* fix(audit): disclose a cleanup that stopped at its per-call ceiling (#5976)\nThe cleanup endpoint deletes at most 500 rows per batch in 20 batches and answered with the\ndeleted count alone, so a sweep that ran out of budget looked exactly like a complete one and the\noperator was told the retention window had been applied while older rows survived.\nCleanup now answers with AuditCleanupResultVO: the same deleted count plus a truncated flag derived\nfrom the per-call row ceiling, and the service logs a warning naming the ceiling when it is hit.\nFixes #5976\n* docs(api-spec): disclose the cleanup ceiling and end the VO with a newline\nThe cleanup response gained a truncated flag meaning the sweep stopped at its\nper-call ceiling; the spec still documented only the deleted count, so a caller\nhad no way to learn the cleanup was partial. Also add the missing trailing\nnewline to AuditCleanupResultVO."
    },
    {
      "commit": "2a6810b99c62f0863f34f1592dc29d68ca0ad3c1",
      "tree": "05b9ba5b6895d57cffc2e1b1384e7b9bf202b6c1",
      "parents": [
        "da34a5890decb280c24816ee3156bf1159de5387"
      ],
      "author": {
        "name": "btlqql",
        "email": "2977859784@qq.com",
        "time": "Mon Oct 26 20:50:33 2026 +0800"
      },
      "committer": {
        "name": "GitHub",
        "email": "noreply@github.com",
        "time": "Mon Oct 26 20:50:33 2026 +0800"
      },
      "message": "fix(instance): stop a cancelled count batch before the second lookup (#7142)\n\n* fix(instance): stop a cancelled count batch before the second lookup (#7077)\nInstanceService.loadCounts started countGroups unconditionally after countTopics. A batch that had\nalready reached its deadline or been cancelled interrupts its worker, but a provider that ignores\nthe interrupt can still return the first count afterwards, so the worker went on to start the\nsecond provider lookup for a response nobody was waiting for.\nloadCounts now checks the worker interrupt flag between the two lookups and throws\nInterruptedException when it is still set. The exception takes the runner\u0027s existing failure path\n(InstanceResourceCountRunner.execute -\u003e CompletedCount.failure), so there is no executor policy\nchange: the caller keeps its existing unavailable response and a healthy batch still performs both\nlookups. Thread.interrupted() consumes the flag the way any InterruptedException producer does, so\nthe pooled worker does not inherit a stale interrupt for its next count task.\nBecause that exception surfaces as a failed outcome, fillCountsInParallel now treats an\nInterruptedException as a cancellation rather than a provider failure: the instance still loses its\ncounts, but the event is logged at debug instead of counting as a failed lookup. A cancelled batch\nis routine, and counting it as a failure would have made the failure signal meaningless.\nThe pre-existing late-result test signalled its \"provider finished\" latch from countGroups and thus\nrequired the unwanted lookup; it now signals that latch when the first lookup returns.\n* test(instance): keep a blank line before the create-instance cases\nMechanical follow-up to the cancelled-batch change: the new case had been appended\nwithout the separating blank line. The extra coverage for the interrupt\nreclassification was dropped on review instead — the runner reports a\ndeadline-cancelled worker as TIMED_OUT/INTERRUPTED with a null failure, so the\ninstanceof-InterruptedException branch is not reachable from that scenario and a\nlog-asserting case could not be made mutation-sensitive."
    },
    {
      "commit": "da34a5890decb280c24816ee3156bf1159de5387",
      "tree": "931d35273681f627f7b598aa3b05f0b7e003a4fa",
      "parents": [
        "65d77f41738286560e80e22a161e13aebbeb3f93"
      ],
      "author": {
        "name": "btlqql",
        "email": "2977859784@qq.com",
        "time": "Mon Oct 26 20:40:18 2026 +0800"
      },
      "committer": {
        "name": "GitHub",
        "email": "noreply@github.com",
        "time": "Mon Oct 26 20:40:18 2026 +0800"
      },
      "message": "fix(acl): keep the stored admin flag on a partial plain-access update (#7139)\n\n* fix(acl): keep the stored admin flag on a partial plain-access update\nadmin is optional in the documented plain-access write contract, but the write path could not tell \"not provided\" from \"explicitly false\": the request DTO and the write model carried a primitive boolean and createAndUpdatePlainAccessConfig always wrote it. Saving an existing admin account without naming the flag - rotating its secret, editing its whitelist or its permission entries - silently demoted it, answered admin:false and dropped it out of the ACL risk view\u0027s admin count. Follow the sibling user update: the flag is nullable on the write model, an omitted flag keeps the stored value for an existing account, a new account defaults to false, an explicit value always wins, and the write response carries the effective value.\n* docs(api-spec): spell out the tri-state admin flag on plain-access upsert\nThe flag became optional with keep-stored semantics; the request row still read as\na plain boolean and the response said nothing about which value comes back."
    },
    {
      "commit": "65d77f41738286560e80e22a161e13aebbeb3f93",
      "tree": "5da69721e12268a8be25dd50211623f8bcd6b04b",
      "parents": [
        "e5def86d7a9d5fe5c17522f5e2bbbd375d12ccde"
      ],
      "author": {
        "name": "btlqql",
        "email": "2977859784@qq.com",
        "time": "Mon Oct 26 20:38:00 2026 +0800"
      },
      "committer": {
        "name": "GitHub",
        "email": "noreply@github.com",
        "time": "Mon Oct 26 20:38:00 2026 +0800"
      },
      "message": "fix(web): drop the client heartbeat column the API can never fill (#7138)\n\n* fix(web): drop the client heartbeat column the API can never fill (#6086)\nThe clients page advertised a connection time in three places -- the table\u0027s\n`Heartbeat` column, the CSV export\u0027s `Connected At` column and the connection detail\ndialog -- all bound to `ClientConnectionVO.connectedAt`, a field no provider ever\nassigns. `RocketMQClientProvider.toConnectionVO(ProducerInfo, ...)` (`:337`) and\n`toConnectionVO(Connection, ...)` (`:469`) build the VO from `ProducerInfo` /\n`Connection` payloads that carry no timestamp, and the proxy branch passes none\neither, so every cell rendered `-`. That reads as \"this client never sent a\nheartbeat\" rather than \"this build does not collect it\".\nDrop the three surfaces instead of inventing a timestamp the brokers do not report --\nthe same resolution #5600 took for the fabricated NameServer `Connections` column.\nThe regression test asserts that the column header, the dialog row and the CSV column\nare all gone, and the existing CSV/dialog expectations are trimmed accordingly.\n* fix(web): drop the i18n key the removed heartbeat column owned\ncluster.heartbeat labelled the connection-time column this PR removes; nothing\nreferences it afterwards, and an orphan key in the bilingual directory is exactly\nthe drift the completeness guard cannot see."
    },
    {
      "commit": "e5def86d7a9d5fe5c17522f5e2bbbd375d12ccde",
      "tree": "fb7671ff170880e123cb73e35fc882d15f19f4f3",
      "parents": [
        "b77dc7dfd7823b760fe8a0cddcfe44231dfe3e7b"
      ],
      "author": {
        "name": "btlqql",
        "email": "2977859784@qq.com",
        "time": "Mon Oct 26 20:35:45 2026 +0800"
      },
      "committer": {
        "name": "GitHub",
        "email": "noreply@github.com",
        "time": "Mon Oct 26 20:35:45 2026 +0800"
      },
      "message": "fix(consumer): report the -1 sentinel when a group enrichment is cancelled by the batch deadline (#7144)\n\n* fix(consumer): report the -1 sentinel when a group enrichment is cancelled by the batch deadline\nThe 3 s batch deadline cancels a group future before it runs, and the untouched initial\nGroupLiveSnapshot.empty() is then applied to the returned ConsumerGroupVO. empty() hard-coded\nonlineInstances 0, so an inventory that was never read reached the API as a measured \"no clients\nonline\" (green 0 instead of 不可用) and could raise a critical \"有堆积但无在线客户端\" alert from no data.\nStart the initial snapshot at the -1 \"inventory unavailable\" sentinel that ConsumerGroupVO\ndocuments and that the failure path already uses, and pin it by extending the existing\ntimedOutGroupEnrichmentMustNotMutateReturnedGroupTest to assert -1. Worker-written snapshots are\nuntouched."
    },
    {
      "commit": "b77dc7dfd7823b760fe8a0cddcfe44231dfe3e7b",
      "tree": "fd7d415e4d5c1a097560cf8b1c1ab51969edec08",
      "parents": [
        "bd12f98fe801ee7eb9511ef527f03f377aa362e5"
      ],
      "author": {
        "name": "btlqql",
        "email": "2977859784@qq.com",
        "time": "Mon Oct 26 20:35:22 2026 +0800"
      },
      "committer": {
        "name": "GitHub",
        "email": "noreply@github.com",
        "time": "Mon Oct 26 20:35:22 2026 +0800"
      },
      "message": "fix(web): reject semantically duplicate alert rules in mock mode (#7140)\n\n* fix(web): reject semantically duplicate alert rules in mock mode (#7078)\nThe mock alert-rule store accepted a second rule whose evaluation fields\n(domain, instance, metric, operator, threshold and threshold unit, duration,\naggregation, window, group, topic, consecutive samples) matched an existing\nrule, so Mock mode reported success where the REST service answers 409\n\"An alert rule with the same evaluation conditions already exists: \u003cname\u003e\".\nPort the server\u0027s AlertRuleSemanticFingerprint normalization (trim, upper-case\noperator/aggregation, lower-case duration, \"%\" to fraction for ratio metrics,\ndefaults for aggregation/window/consecutive samples) into the mock create path\nand reject a matching rule with the same message. The display name is not part\nof the fingerprint, so renaming a rule no longer hides the conflict.\nTwo existing mock tests created rules with identical (empty) evaluation fields\nand would now collide, so they set distinct metrics; their assertions are\nunchanged."
    },
    {
      "commit": "bd12f98fe801ee7eb9511ef527f03f377aa362e5",
      "tree": "0bb4134b4d77daff485c460062c402f715881268",
      "parents": [
        "b03d21c089bf7828cadbbc1600afcf658769412a"
      ],
      "author": {
        "name": "btlqql",
        "email": "2977859784@qq.com",
        "time": "Mon Oct 26 20:35:00 2026 +0800"
      },
      "committer": {
        "name": "GitHub",
        "email": "noreply@github.com",
        "time": "Mon Oct 26 20:35:00 2026 +0800"
      },
      "message": "fix(litetopic): report partial multi-master TTL updates instead of a bare write error (#7137)\n\n* fix(litetopic): report partial multi-master TTL updates instead of a bare write error (#7085)\nextendTTL writes lite.topic.expiration to every broker master that carries the Lite parent\ntopic. When a later write failed, the earlier masters kept the new expiration but the caller\nonly received the raw write error, so a cluster could stay split between the old and the new\nTTL with nothing in the response saying which masters had been changed.\nThe write phase now tracks the masters it already updated and, on a failure after the first\none, throws a 502 BusinessException that names the updated masters, the failed master with its\nerror and the masters that were not attempted, plus the recovery path: the extension is\nidempotent, so re-running it converges every master. A failure on the first write still\npropagates unchanged and the all-master success path is untouched."
    },
    {
      "commit": "b03d21c089bf7828cadbbc1600afcf658769412a",
      "tree": "0ca1edb5a78613fd9b52e6c78cca65467c963691",
      "parents": [
        "8bfdef573f292665c8216597ec64af6b7d5d2552"
      ],
      "author": {
        "name": "btlqql",
        "email": "2977859784@qq.com",
        "time": "Mon Oct 26 20:34:35 2026 +0800"
      },
      "committer": {
        "name": "GitHub",
        "email": "noreply@github.com",
        "time": "Mon Oct 26 20:34:35 2026 +0800"
      },
      "message": "fix(alert): answer a rule update with the stored rule (#7136)\n\n* fix(alert): answer a rule update with the stored rule\nupdateRule returned the request-derived rule, so the edit response never carried lastTriggered: the repository deliberately keeps the stored fire history, and the Alerts page replaces the edited row with that response. A rule that had fired therefore flipped to \"Never Triggered\" until the list reloaded, while toggling the same rule brought the stamp back because the toggle path answers with the stored row. Read the stored rule back after the write and return it; the audit call and the rest of the update are unchanged."
    },
    {
      "commit": "8bfdef573f292665c8216597ec64af6b7d5d2552",
      "tree": "9b901b63dfe8bf0eee9a2dc10cd1b9bc37b195b6",
      "parents": [
        "f24be26c58933f7017f9f9986f5cb862348cdb9e"
      ],
      "author": {
        "name": "btlqql",
        "email": "2977859784@qq.com",
        "time": "Mon Oct 26 20:34:09 2026 +0800"
      },
      "committer": {
        "name": "GitHub",
        "email": "noreply@github.com",
        "time": "Mon Oct 26 20:34:09 2026 +0800"
      },
      "message": "fix(rmqctl): accept scoped IPv6 link-local Studio endpoints (#7135)\n\n* fix(rmqctl): accept scoped IPv6 link-local Studio endpoints (#7131)\nurl.Parse reports the hostname of an escaped scoped literal such as\nhttp://[fe80::1%25eth0]:6789 as fe80::1%eth0, zone included. isPrivateHost\nclassified that host with net.ParseIP, which rejects the \"%eth0\" suffix, so\nevery scoped loopback, link-local or private IPv6 endpoint was treated as a\npublic host and plain HTTP was refused before any request was dispatched.\nClassify the host with net/netip instead: it keeps the zone and lets the\naddress be judged by its actual bits, so scoped loopback, link-local and\nRFC1918/ULA addresses are accepted like their unscoped forms. IPv4-mapped\naddresses are unmapped to stay classified like their IPv4 form. The zone is\nleft untouched in the outgoing URL (still escaped as \"%25eth0\"), and public\nor malformed hosts are still rejected."
    },
    {
      "commit": "f24be26c58933f7017f9f9986f5cb862348cdb9e",
      "tree": "1854f73effa2730477b7c72fb0a9619ccead6426",
      "parents": [
        "6024b7bfed0a9b647f1a4061cd71cd2787320055"
      ],
      "author": {
        "name": "messere",
        "email": "731107711@qq.com",
        "time": "Mon Oct 26 20:32:56 2026 +0800"
      },
      "committer": {
        "name": "GitHub",
        "email": "noreply@github.com",
        "time": "Mon Oct 26 20:32:56 2026 +0800"
      },
      "message": "fix(producer): invalidate connection queries when the group changes (#7208)\n\n* fix(producer): invalidate connection queries when the group changes"
    },
    {
      "commit": "6024b7bfed0a9b647f1a4061cd71cd2787320055",
      "tree": "4ca3c0006c1da5d2f2fa59fef24dfc01e763c9c3",
      "parents": [
        "8cafab339f57d3cc5b99f808241c4e0f26aee04c"
      ],
      "author": {
        "name": "messere",
        "email": "731107711@qq.com",
        "time": "Mon Oct 26 20:32:39 2026 +0800"
      },
      "committer": {
        "name": "GitHub",
        "email": "noreply@github.com",
        "time": "Mon Oct 26 20:32:39 2026 +0800"
      },
      "message": "fix(acl): skip broker push rules without a resource type instead of widening them (#6727)\n\n* fix(acl): skip broker push rules without a resource type instead of widening them"
    },
    {
      "commit": "8cafab339f57d3cc5b99f808241c4e0f26aee04c",
      "tree": "a72a07effc71773ae588fdf03809f11fd16f2657",
      "parents": [
        "b300350278ef72a1240fad13738ddc599a2a2eb0"
      ],
      "author": {
        "name": "messere",
        "email": "731107711@qq.com",
        "time": "Mon Oct 26 20:32:02 2026 +0800"
      },
      "committer": {
        "name": "GitHub",
        "email": "noreply@github.com",
        "time": "Mon Oct 26 20:32:02 2026 +0800"
      },
      "message": "fix(web): lock repeat confirmations on broker config and data source writes (#7199)\n\nIntegration batch of two same-template fixes by messere1: the broker config submit (#7199) and the data source create/update submit (#7200) both raced a second confirmation click fired before React re-rendered the submitting state, putting the same write on the wire twice. Each now takes a synchronous ref lock, following the nsSubmittingRef convention. The cluster half is adapted to the submit/applyConfigUpdate split that landed on trunk after the PR was written: the lock is taken in handleConfigSubmit and released by the write\u0027s finally, the early returns and the flush-disk confirmation\u0027s cancel. #7200 is closed as folded into this carrier; both commits keep their original author."
    },
    {
      "commit": "b300350278ef72a1240fad13738ddc599a2a2eb0",
      "tree": "b3708fae2c408eb3f28eaa21978acd498bb4424c",
      "parents": [
        "f6decc63cbd398301b78449115aa5baafdf657d5"
      ],
      "author": {
        "name": "messere",
        "email": "731107711@qq.com",
        "time": "Mon Oct 26 20:15:41 2026 +0800"
      },
      "committer": {
        "name": "GitHub",
        "email": "noreply@github.com",
        "time": "Mon Oct 26 20:15:41 2026 +0800"
      },
      "message": "fix(web): surface failed mutations instead of swallowing the rejection (#7105)\n\nIntegration batch of three same-pattern fixes by messere1: a rejected bulk rule delete (#7045), a rejected consumer group delete (#7046) and a rejected proxy restart (#7105) all disappeared into antd\u0027s confirm wrapper, leaving the operator with no outcome. Each handler now reports the failure with its own message and keeps the selection armed where a retry makes sense. Commits keep their original author; #7045 and #7046 are closed as folded into this carrier."
    },
    {
      "commit": "f6decc63cbd398301b78449115aa5baafdf657d5",
      "tree": "44f2732b1dc634cc399928da25f3b8f598f27b93",
      "parents": [
        "b88bc2fb8268b1b6c5a98b6d7eb746e82e9a5525"
      ],
      "author": {
        "name": "coder999o",
        "email": "soft_99@126.com",
        "time": "Mon Oct 26 20:09:23 2026 +0800"
      },
      "committer": {
        "name": "GitHub",
        "email": "noreply@github.com",
        "time": "Mon Oct 26 20:09:23 2026 +0800"
      },
      "message": "fix(k8s): sort certificate expiry timestamps as UTC (#7319)\n\n* fix(k8s): sort certificate expiry timestamps as UTC"
    },
    {
      "commit": "b88bc2fb8268b1b6c5a98b6d7eb746e82e9a5525",
      "tree": "0bac999ea5f2e117989e736d8906b2469701b776",
      "parents": [
        "505f0ed0b680e91d1aef7515940e88fea5756226"
      ],
      "author": {
        "name": "coder999o",
        "email": "soft_99@126.com",
        "time": "Mon Oct 26 20:09:00 2026 +0800"
      },
      "committer": {
        "name": "GitHub",
        "email": "noreply@github.com",
        "time": "Mon Oct 26 20:09:00 2026 +0800"
      },
      "message": "fix(acl): report unrestricted account sources in risk diagnostics (#7291)\n\n* fix(acl): report unrestricted account sources in risk diagnostics"
    },
    {
      "commit": "505f0ed0b680e91d1aef7515940e88fea5756226",
      "tree": "a96711dec6d8eef4d7df16f90b6c5e9ca2237681",
      "parents": [
        "9cb68fe50c0c643150ede2ab78329fd51c3052b8"
      ],
      "author": {
        "name": "coder999o",
        "email": "soft_99@126.com",
        "time": "Mon Oct 26 20:08:37 2026 +0800"
      },
      "committer": {
        "name": "GitHub",
        "email": "noreply@github.com",
        "time": "Mon Oct 26 20:08:37 2026 +0800"
      },
      "message": "fix(settings): reflect active appearance preferences in controls (#7219)\n\n* fix(settings): reflect active appearance preferences in controls"
    },
    {
      "commit": "9cb68fe50c0c643150ede2ab78329fd51c3052b8",
      "tree": "069f00177e53606683a9f1abbbd7b20907b02897",
      "parents": [
        "1d619ee56ef95c4122c1350db241b7d8afa69b03"
      ],
      "author": {
        "name": "Chstercc",
        "email": "165463128+chstercc@users.noreply.github.com",
        "time": "Mon Oct 26 20:07:02 2026 +0800"
      },
      "committer": {
        "name": "GitHub",
        "email": "noreply@github.com",
        "time": "Mon Oct 26 20:07:02 2026 +0800"
      },
      "message": "fix(web): render an absent trace or consumption time as a placeholder (#6929)\n\n* fix(web): remove clear controls from always-applied range pickers\nThe topic query, the DLQ export and the DLQ resend always send a full\nstart/end pair, so their pickers cannot represent a cleared value: the\nclear icon snapped the value back and silently reused the previous\nwindow. Disable that control on the three pickers and pin the behaviour\nwith regressions.\n* fix(web): render an absent trace or consumption time as a placeholder\nTrace nodes and consumer status report \u0027no time\u0027 as 0. The shared\nformatTimeMs treats zero as a real timestamp because stored values can\nlegitimately be the epoch, so both call sites rendered 1970-01-01 for a\nrecord that carried no timestamp at all.\nMap zero to the placeholder at the optional-time call sites while\nleaving formatTimeMs\u0027 epoch handling unchanged.\n* refactor(web): move the optional-time formatter next to formatTimeMs\nThe zero-means-absent mapping is a property of the time formatter, not of the\nmessage page; keeping it in utils/format lets the next optional-time call site\nreuse it instead of re-deriving the epoch caveat."
    },
    {
      "commit": "1d619ee56ef95c4122c1350db241b7d8afa69b03",
      "tree": "a9cda1bda0444eba0641b01b6959af7d9d92186c",
      "parents": [
        "9d22a272942a92f3402ebb365d41578a87c731fa"
      ],
      "author": {
        "name": "lizhimins",
        "email": "707364882@qq.com",
        "time": "Mon Oct 26 19:53:54 2026 +0800"
      },
      "committer": {
        "name": "GitHub",
        "email": "noreply@github.com",
        "time": "Mon Oct 26 19:53:54 2026 +0800"
      },
      "message": "fix(web): drop the node_modules symlink from #5895 and surface refused credential exports (#7357)\n\n* fix(web): drop the node_modules symlink that rode in with #5895\n\nThe audit export fix carried a committed symlink at web/node_modules pointing at\nthe author\u0027s own worktree (/home/xiaoyang/rmq-worktrees/pr5926/web/node_modules).\nA checkout on any other machine gets a dangling link where the package tree must\nbe, and the directory is gitignored precisely so it never ships. Remove it from\nthe index; the real directory stays untracked.\n\n* fix(settings): surface the server reason on a refused credential export too\n\nThe audit export got the describeApiError treatment in the previous commit; the\ncloud credential export still swallowed the error object and advised a retry that\na refused export can never survive. Same extractor, same fallback string, plus a\ncase that pins the server message reaching the toast instead of the generic retry\nadvice."
    },
    {
      "commit": "9d22a272942a92f3402ebb365d41578a87c731fa",
      "tree": "fae72d0b755b097c33016cdb32e7d98a9671b44c",
      "parents": [
        "bf4ff9e2994652bc7454f616e273f6c4106be625"
      ],
      "author": {
        "name": "烤化の初雪",
        "email": "yangzihao_41@outlook.com",
        "time": "Mon Oct 26 19:50:30 2026 +0800"
      },
      "committer": {
        "name": "GitHub",
        "email": "noreply@github.com",
        "time": "Mon Oct 26 19:50:30 2026 +0800"
      },
      "message": "fix(audit): surface the export cap instead of advising a retry that cannot work (#5895)\n\n* fix(audit): surface the export cap instead of advising a retry that cannot work\nGET /audit-logs/export refuses any filtered result set above\nAuditService.MAX_EXPORT_RECORDS with\n    400 {\"code\":400,\"message\":\"Audit log export exceeds the maximum of 10000\n    records; narrow the filters\"}\nThe audit page\u0027s export handler caught the failure without the error object and\nshowed its generic \"导出审计日志失败，请稍后重试\" (\"... try again later\"), which is\nadvice that can never succeed: the same filters produce the same 400. The one\nactionable fact - narrow the filters - is the only thing the toast drops.\nThe utilities the fix needs already exist: exportAuditLogs is a JSON call, so\nerror.response.data.message carries the reason, and describeApiError is the\nshared extractor the alert and instance pages use for exactly this (they surface\nalert-rule import conflicts the same way). Fall back to the existing string when\nthe failure carries no server message.\nTests (web/): npx vitest run src/pages/ops/__tests__/AuditPage.test.tsx\n- new case \"surfaces the export cap instead of telling the user to retry\" fails\n  before this change (the cap message is absent and the generic toast is shown)\n  and passes after it; 12 tests in the file pass.\n- npx eslint on both files reports no errors; npx tsc -b passes."
    },
    {
      "commit": "bf4ff9e2994652bc7454f616e273f6c4106be625",
      "tree": "40970cf44df67cff050f6ef39884d35fce88df36",
      "parents": [
        "fb06d98f9ba36d8ae3f4258483b818dd2556fec3"
      ],
      "author": {
        "name": "nawei123",
        "email": "160563755+nawei123@users.noreply.github.com",
        "time": "Mon Oct 26 19:48:07 2026 +0800"
      },
      "committer": {
        "name": "GitHub",
        "email": "noreply@github.com",
        "time": "Mon Oct 26 19:48:07 2026 +0800"
      },
      "message": "fix(alerts): initialize legacy ratio thresholds before discovery (#6920)\n\n* fix(alerts): initialize legacy ratio thresholds before discovery"
    },
    {
      "commit": "fb06d98f9ba36d8ae3f4258483b818dd2556fec3",
      "tree": "17c5cbf747870ee055e02b1664a5eb6c50aeaa0b",
      "parents": [
        "33b5407464542ea0aacc4330376c5e8515e9b006"
      ],
      "author": {
        "name": "lizhimins",
        "email": "707364882@qq.com",
        "time": "Mon Oct 26 19:03:41 2026 +0800"
      },
      "committer": {
        "name": "GitHub",
        "email": "noreply@github.com",
        "time": "Mon Oct 26 19:03:41 2026 +0800"
      },
      "message": "fix(web): repair the user management page after the account lifecycle merge (#7356)\n\nA full-suite run on the merged trunk surfaced two breakages that neither side of\nthe merge showed on its own:\n\n- UserManagement still called setSessionDrawerUser in the delete and revoke-all\n  paths, a name the drawer-to-modal round replaced with setSessionModalUser, so\n  tsc -b failed the frontend build gate.\n- UserManagementModalValidation mocked api/studioUsers before the account lifecycle\n  added five functions to it. The page awaits listStudioLoginLockouts inside\n  Promise.all, so the missing mock made the whole load reject and the table\n  rendered no rows — the \"Password\" action the case clicks never appeared. Mock the\n  five, and give the lockouts call a resolved promise rather than undefined."
    },
    {
      "commit": "33b5407464542ea0aacc4330376c5e8515e9b006",
      "tree": "ae6d0ad8f735d0ed7db8db2db61ae5ff34e8f39c",
      "parents": [
        "5e6c8971e6a75d7fb128198e54965c34fc0764f0"
      ],
      "author": {
        "name": "xdz",
        "email": "xinqiang@isrc.iscas.ac.cn",
        "time": "Mon Oct 26 18:04:45 2026 +0800"
      },
      "committer": {
        "name": "GitHub",
        "email": "noreply@github.com",
        "time": "Mon Oct 26 18:04:45 2026 +0800"
      },
      "message": "feat(auth): complete the studio account lifecycle: role change, deletion, lockout visibility, forced rotation (#7312)\n\n* feat(auth): complete the studio account lifecycle: role change, deletion, lockout visibility, forced rotation\nCompletes the studio account lifecycle: guarded role changes and deletions, login-lockout visibility for administrators, and forced rotation of passwords chosen by someone else.\n* fix(auth): enforce the outstanding password change on the server too\nThe rotation was only prompted by the login page, so an API client that held on to\nthe token issued at login could use the whole surface with a credential somebody\nelse chose. Refuse every path except the rotation itself and logout while the flag\nis set — 403 \"Password change required\" — and cover it with an interceptor case that\ngoes red the moment the gate stops applying to administrators.\nAlso document the three new endpoints in api-spec.md §17.4-17.6 (role change, account\ndeletion, login lockout snapshot) with their 400 and 409 boundaries, and widen §17 to\nthe account surface they belong to."
    },
    {
      "commit": "5e6c8971e6a75d7fb128198e54965c34fc0764f0",
      "tree": "88d9a1505280fb34984b97864002b4d467542153",
      "parents": [
        "7bbbea5581890c6ebe2a9e61d1e8b4c39f7cb745"
      ],
      "author": {
        "name": "xdz999",
        "email": "tju_yxq@qq.com",
        "time": "Mon Oct 26 17:48:34 2026 +0800"
      },
      "committer": {
        "name": "GitHub",
        "email": "noreply@github.com",
        "time": "Mon Oct 26 17:48:34 2026 +0800"
      },
      "message": "feat(auth): complete the session lifecycle: idle timeout, revocation controls, client attribution, expiry redirect (#7311)\n\n* feat(auth): complete the session lifecycle: idle timeout, revocation controls, client attribution, expiry redirect\nCompletes the session lifecycle: a configurable idle timeout that revokes abandoned sessions, per-session and global revocation controls, client attribution on every session, and a return-to-page redirect after an expiry.\n* fix(auth): leave the idle deadline off by default and out of the shipped profiles\nThe idle timeout defaulted to 30 minutes, so an upgrade immediately started revoking\nsessions that were issued under the old absolute-expiry-only rule. Default it to 0 —\noff — and let an operator turn it on. The settings form already accepts 0 and says\nwhat it means; its fallback now matches the server instead of showing 30.\nThe scheduled purge had also gained an initialDelay, and application-dev.yml stretched\nthe cleanup interval to a day so the auth integration tests would not see a pass fire\nmid-suite. Both are reverted: the cases that exercise the idle deadline now configure\none, which is what makes them about the feature rather than about the default.\nDocument in api-spec.md the two new endpoints — POST\n/api/studio-users/sessions/revoke-all, which spares the caller\u0027s own sessions, and\nDELETE /api/studio-users/sessions/:sessionId, a no-op on an already revoked row —\nplus the sessionIdleTimeout setting and the clientIp / userAgent attribution fields."
    },
    {
      "commit": "7bbbea5581890c6ebe2a9e61d1e8b4c39f7cb745",
      "tree": "c51ad19078aee9d974c1e90735b9660598c8ed73",
      "parents": [
        "799f459d11e90fc4c6864973e0d9fd83924a68b0"
      ],
      "author": {
        "name": "xdz",
        "email": "xinqiang@isrc.iscas.ac.cn",
        "time": "Mon Oct 26 17:29:47 2026 +0800"
      },
      "committer": {
        "name": "GitHub",
        "email": "noreply@github.com",
        "time": "Mon Oct 26 17:29:47 2026 +0800"
      },
      "message": "fix(consumer): filter the group page by subscription mode server-side (#7310)\n\n* fix(consumer): filter the group page by subscription mode server-side\nFilters the consumer group page by subscription mode on the server side, instead of filtering only the currently loaded page in the browser.\n* fix(consumer): match the subscription mode filter to what the list displays\nparseSubscriptionMode answers Push for every stored value that is not Pop — a null,\na blank, or a legacy \"CLUSTERING\" — but the paged query filtered with\nmessage_model \u003d \u0027Push\u0027, so exactly those rows vanished from the filtered page while\nthe unfiltered list still labelled them Push. Pop stays an equality; Push becomes\n\"not Pop\", and the requested mode is canonicalised so the in-memory inventory path\nand the SQL path cannot disagree on spelling.\nCover the two shapes in the provider test, and document subscriptionMode on\nGET /api/groups/page."
    },
    {
      "commit": "799f459d11e90fc4c6864973e0d9fd83924a68b0",
      "tree": "70ed0a62d0d7bd415c3dbe708bec8e4f97ffa547",
      "parents": [
        "4a1020b08fbff3e05c20305ddfcf8242b8574205"
      ],
      "author": {
        "name": "xdz",
        "email": "xinqiang@isrc.iscas.ac.cn",
        "time": "Mon Oct 26 17:19:46 2026 +0800"
      },
      "committer": {
        "name": "GitHub",
        "email": "noreply@github.com",
        "time": "Mon Oct 26 17:19:46 2026 +0800"
      },
      "message": "feat(audit): add an operator filter to the audit feed (#7308)\n\n* fix(audit): align the operator filter with the current audit surfaces\n* fix(audit): restore the missing-cluster coverage the operator filter dropped\nThe operator filter arrived with three repository cases and one controller case\ndeleted rather than updated, and AuditServiceTest still called the pre-operator\nsignatures, so the branch did not compile. Restore all four cases against the new\narity and add the equality assertion the operator filter needs — a LIKE would also\nmatch every \"ops-zhang2\" row and leak one operator\u0027s trail into another\u0027s filter.\nAlso repair the findPage and applyFilters indentation the edit broke, and document\nthe operator parameter plus the operators filter option in api-spec.md."
    },
    {
      "commit": "4a1020b08fbff3e05c20305ddfcf8242b8574205",
      "tree": "00503ac6c524f31a8af65becd96f7f0714587ecb",
      "parents": [
        "462cf5912be3b5b8d6104a9f59cd0218397181fd"
      ],
      "author": {
        "name": "aias00",
        "email": "liuhongyu@apache.org",
        "time": "Mon Oct 26 17:09:22 2026 +0800"
      },
      "committer": {
        "name": "GitHub",
        "email": "noreply@github.com",
        "time": "Mon Oct 26 17:09:22 2026 +0800"
      },
      "message": "feat(ai): include bounded prose history in HTTP conversations (#7051)\n\n* feat(ai): include bounded prose history in HTTP conversations\n* refactor(ai): fix the HTTP history flag at admission and answer budget breaches with 400\nRunContext kept every input final except httpHistoryEnabled, which AiRunService set\nthrough a Lombok setter immediately after construction; make it the last constructor\nargument so a run\u0027s shape cannot change mid-flight.\nThe two history budget guards threw IllegalArgumentException where the neighbouring\nAiPayloadGuard checks answer with BusinessException(400), so an over-budget history\nreached the operator as a 500.\nAlso document the HTTP side of `resume` in api-spec.md: for the http engine the flag\ndecides whether the turn carries bounded history (200 events scanned, at most 16\nturns and 64 KiB), history never gains the system prompt\u0027s authority, an over-budget\nhistory is a 400, and resume\u003dfalse applies to that request only."
    },
    {
      "commit": "462cf5912be3b5b8d6104a9f59cd0218397181fd",
      "tree": "b792b81c20a930b2325a0ad0e91502ac2713902b",
      "parents": [
        "6d78d89644564978b193ea5b13a669e486f72ab1"
      ],
      "author": {
        "name": "Xiao Yang",
        "email": "yangx_soft@163.com",
        "time": "Mon Oct 26 17:00:39 2026 +0800"
      },
      "committer": {
        "name": "GitHub",
        "email": "noreply@github.com",
        "time": "Mon Oct 26 17:00:39 2026 +0800"
      },
      "message": "fix(proxy): scope discovery to the configured broker cluster (#7282)\n\n* fix(proxy): scope discovery to the configured broker cluster\n* fix(proxy): answer an unreadable membership with an empty discovery\ndiscoverClustersAt answers a topology it cannot read with an empty list, while the\nscoped proxy lookup threw 503 for the same missing data — one cluster the NameServer\ncould not describe failed the whole page. Only an RPC failure throws now, which is\nthe admin query caliber the rest of the provider keeps (a business-empty result is\na 200 with an empty list)."
    },
    {
      "commit": "6d78d89644564978b193ea5b13a669e486f72ab1",
      "tree": "c5f031cc20d1cc77ea0ece1f5d838023c6751778",
      "parents": [
        "a2d9b00d48a31964bdc95a73cd9faf43ecd36a8b"
      ],
      "author": {
        "name": "ASUKA",
        "email": "asukaaaaa1204@gmail.com",
        "time": "Mon Oct 26 16:55:41 2026 +0800"
      },
      "committer": {
        "name": "GitHub",
        "email": "noreply@github.com",
        "time": "Mon Oct 26 16:55:41 2026 +0800"
      },
      "message": "fix(ops): reject mock alert silences with an invalid time range (#7117)\n\n* fix(ops): reject mock alert silences with an invalid time range\nThe Mock branch of createAlertSilence stored any window it was given, while\nthe REST path rejects a silence whose end is not after its start with\n\"Silence end time must be after start time\". Demo mode could therefore report\na maintenance window the deployed service would never create.\nEnforce the same strictly-after ordering before the Mock inventory is mutated,\nso a rejected request leaves it unchanged. Valid windows still resolve and\nstore exactly as before.\n* fix(ops): mirror the recurrence-end rule in the mock silence create\nThe mock only checked that the window ends after it starts, so a recurring silence\nwith a missing or too-early recurrenceUntil was accepted locally and then rejected\nby AlertSilenceService.validateRecurrence the moment it met a real backend. Mirror\nboth branches and cover them with one case."
    },
    {
      "commit": "a2d9b00d48a31964bdc95a73cd9faf43ecd36a8b",
      "tree": "a7ee427191e563a012db471cb98e5ff5eeec2756",
      "parents": [
        "88b7f3c3b0472b41ff882e8df398534925d0a084"
      ],
      "author": {
        "name": "xdz",
        "email": "xinqiang@isrc.iscas.ac.cn",
        "time": "Mon Oct 26 16:52:05 2026 +0800"
      },
      "committer": {
        "name": "GitHub",
        "email": "noreply@github.com",
        "time": "Mon Oct 26 16:52:05 2026 +0800"
      },
      "message": "fix(alert): bound the system-alert listing by reusing the paginated query (#7306)\n\n* fix(alert): bound the system-alert listing by reusing the paginated query\nGET /api/system-alerts loaded every matching alert into memory and filtered domain/instanceId/transition in Java. rmq_system_alert has no retention sweep, so the table only grows and the unbounded read grew with it. The paginated query already runs every filter in SQL, so the listing now reuses it with the first page at the pagination ceiling: the response stays a plain list bounded to the newest 100 matching alerts, and the page endpoint remains the complete view. The unbounded repository read and its now-unused service entry point are removed; their normalization coverage moves to the paginated query\u0027s tests.\n* docs(api-spec): bound the system-alert listing at the newest 100\nGET /api/system-alerts now reuses the paginated query, so its plain-list response\ncarries at most the newest 100 matching alerts; the spec still read as if every\nmatch came back. Point the complete view at the paginated endpoint."
    },
    {
      "commit": "88b7f3c3b0472b41ff882e8df398534925d0a084",
      "tree": "714357fd52c515dab5354a0f9a980f5b77c53aee",
      "parents": [
        "b739b6a7bf58643932dd16cc0d0fcdd070a7568f"
      ],
      "author": {
        "name": "Xiao Yang",
        "email": "yangx_soft@163.com",
        "time": "Mon Oct 26 16:48:46 2026 +0800"
      },
      "committer": {
        "name": "GitHub",
        "email": "noreply@github.com",
        "time": "Mon Oct 26 16:48:46 2026 +0800"
      },
      "message": "fix(topic): preserve live permissions when updates omit them (#7096)\n\n* fix(topic): preserve live permissions when updates omit them\n* docs(api-spec): state the omitted-permission rule in the table\u0027s own language\nThe perm row of POST /api/topics/update was replaced by a full English paragraph\ninside an otherwise Chinese table. Same semantics, matching the surrounding cells."
    },
    {
      "commit": "b739b6a7bf58643932dd16cc0d0fcdd070a7568f",
      "tree": "c0db605979ea6657441e0eb65bd1ff21a767d1f9",
      "parents": [
        "6ce131b1f77c76a03dc5e9b6cfafb2e745f251fc"
      ],
      "author": {
        "name": "wizcraft_kris",
        "email": "99409434+Kris20030907@users.noreply.github.com",
        "time": "Mon Oct 26 16:46:05 2026 +0800"
      },
      "committer": {
        "name": "GitHub",
        "email": "noreply@github.com",
        "time": "Mon Oct 26 16:46:05 2026 +0800"
      },
      "message": "fix(ai): stamp gmt_modified when forgetting the runtime session (#7050)\n\n* fix(ai): stamp gmt_modified when forgetting the runtime session\n* test(ai): pin the executor clock so the forgotten-session stamp is asserted\nBoth resume-recovery cases verified clearRuntimeSessionId with\nany(LocalDateTime.class), which also passes a stamp read from the database\nsession\u0027s timezone — the exact skew the caller-supplied clock exists to avoid.\nGive the shared executor a fixed clock and assert the stamp it must carry:\nreplacing now(clock) with now() in AiRunExecutor now turns both cases red."
    },
    {
      "commit": "6ce131b1f77c76a03dc5e9b6cfafb2e745f251fc",
      "tree": "0373ab558a81a046db5b521586f0bb683c28fe76",
      "parents": [
        "6a103f24cac2d16801ab8a095d264a6115ddd88e"
      ],
      "author": {
        "name": "Xiao Yang",
        "email": "yangx_soft@163.com",
        "time": "Mon Oct 26 16:38:25 2026 +0800"
      },
      "committer": {
        "name": "GitHub",
        "email": "noreply@github.com",
        "time": "Mon Oct 26 16:38:25 2026 +0800"
      },
      "message": "fix(acl): reject zero-only plain address ranges (#7315)\n\n* fix(acl): reject zero-only plain address ranges"
    },
    {
      "commit": "6a103f24cac2d16801ab8a095d264a6115ddd88e",
      "tree": "429b64d23738c2e1d1b514ca07c461ede9b2a171",
      "parents": [
        "0ae28e02c612ad64d2faaf2c9faa2bc9fb01725c"
      ],
      "author": {
        "name": "Uğur Tafralı",
        "email": "tafraliugur@gmail.com",
        "time": "Mon Oct 26 11:38:01 2026 +0300"
      },
      "committer": {
        "name": "GitHub",
        "email": "noreply@github.com",
        "time": "Mon Oct 26 16:38:01 2026 +0800"
      },
      "message": "fix: preserve custom property whitespace (#7304)\n\n* fix: preserve custom property whitespace\nCustom property values were being trimmed of all whitespace, which corrupts data when users intentionally include leading/trailing spaces or full-width spaces. The fix only trims values that are entirely whitespace, preserving the original otherwise."
    },
    {
      "commit": "0ae28e02c612ad64d2faaf2c9faa2bc9fb01725c",
      "tree": "f58fe5c3cfe7b40b05853caaefd77336b0987e02",
      "parents": [
        "57b93490da2558c906c8aa9b43c9f7a797e4b149"
      ],
      "author": {
        "name": "pilichoumao",
        "email": "99873428+pilichoumao@users.noreply.github.com",
        "time": "Mon Oct 26 16:37:37 2026 +0800"
      },
      "committer": {
        "name": "GitHub",
        "email": "noreply@github.com",
        "time": "Mon Oct 26 16:37:37 2026 +0800"
      },
      "message": "fix(metrics): normalize range query steps before transport (#7279)\n\n* fix(metrics): normalize range query steps before transport"
    },
    {
      "commit": "57b93490da2558c906c8aa9b43c9f7a797e4b149",
      "tree": "19cb9b48540b49627c8c199769b49450c3eda54e",
      "parents": [
        "37fa6f8d7c308e936de2dbd5fdb4412905e91c4c"
      ],
      "author": {
        "name": "pilichoumao",
        "email": "99873428+pilichoumao@users.noreply.github.com",
        "time": "Mon Oct 26 16:37:14 2026 +0800"
      },
      "committer": {
        "name": "GitHub",
        "email": "noreply@github.com",
        "time": "Mon Oct 26 16:37:14 2026 +0800"
      },
      "message": "feat(tools): expose bounded DLQ resend failure details (#7278)\n\n* feat(tools): expose bounded DLQ resend failure details\n* docs(tools): place DLQ output notes after resend constraints\n* docs(tools): preserve existing DLQ paragraph spacing"
    },
    {
      "commit": "37fa6f8d7c308e936de2dbd5fdb4412905e91c4c",
      "tree": "f44691f22fbb8c496ba198ef22f8a760a830cc04",
      "parents": [
        "6f5535c576f6054d14fc6616a6b5216dee0fd552"
      ],
      "author": {
        "name": "pilichoumao",
        "email": "99873428+pilichoumao@users.noreply.github.com",
        "time": "Mon Oct 26 16:36:52 2026 +0800"
      },
      "committer": {
        "name": "GitHub",
        "email": "noreply@github.com",
        "time": "Mon Oct 26 16:36:52 2026 +0800"
      },
      "message": "fix(dlq): preserve Unicode boundaries in resend failures (#7275)\n\n* fix(dlq): preserve Unicode boundaries in resend failures"
    },
    {
      "commit": "6f5535c576f6054d14fc6616a6b5216dee0fd552",
      "tree": "4710aa7ef9819370f725ea16b0379d862ca18a14",
      "parents": [
        "9ec3a5914dd54f88e119284677b2d3d51f2201cb"
      ],
      "author": {
        "name": "aias00",
        "email": "liuhongyu@apache.org",
        "time": "Mon Oct 26 16:35:28 2026 +0800"
      },
      "committer": {
        "name": "GitHub",
        "email": "noreply@github.com",
        "time": "Mon Oct 26 16:35:28 2026 +0800"
      },
      "message": "fix(nameserver): make registry replacements atomic (#7231)\n\n* fix(nameserver): make registry replacements atomic"
    },
    {
      "commit": "9ec3a5914dd54f88e119284677b2d3d51f2201cb",
      "tree": "86dc17b94805dfaff3837b0e86129c77d5ad8e41",
      "parents": [
        "3c1382a28823a3a3dcfbef152df5db68c9ff1c7a"
      ],
      "author": {
        "name": "aias00",
        "email": "liuhongyu@apache.org",
        "time": "Mon Oct 26 16:35:06 2026 +0800"
      },
      "committer": {
        "name": "GitHub",
        "email": "noreply@github.com",
        "time": "Mon Oct 26 16:35:06 2026 +0800"
      },
      "message": "fix(auth): preserve monotonic session activity timestamps (#7230)\n\n* fix(auth): preserve monotonic session activity timestamps\n* test(auth): use explicit activity timestamps across timezones"
    },
    {
      "commit": "3c1382a28823a3a3dcfbef152df5db68c9ff1c7a",
      "tree": "597261968566dac52f9f502f2ca3aa978467ef8e",
      "parents": [
        "d712772bc08c21facd01d5c3436fa7a2dd922d83"
      ],
      "author": {
        "name": "aias00",
        "email": "liuhongyu@apache.org",
        "time": "Mon Oct 26 16:34:45 2026 +0800"
      },
      "committer": {
        "name": "GitHub",
        "email": "noreply@github.com",
        "time": "Mon Oct 26 16:34:45 2026 +0800"
      },
      "message": "fix(alerts): isolate bulk rule item transactions (#7221)\n\n* fix(alerts): isolate bulk rule item transactions"
    },
    {
      "commit": "d712772bc08c21facd01d5c3436fa7a2dd922d83",
      "tree": "3482e7e5c2e36fa251b67abcc513d8e61ab59369",
      "parents": [
        "52e8f9c48722139499f1e3d7dd4ddf0dbdf9e665"
      ],
      "author": {
        "name": "aias00",
        "email": "liuhongyu@apache.org",
        "time": "Mon Oct 26 16:33:37 2026 +0800"
      },
      "committer": {
        "name": "GitHub",
        "email": "noreply@github.com",
        "time": "Mon Oct 26 16:33:37 2026 +0800"
      },
      "message": "fix(ai): enforce a shared run deadline across provider stages (#7044)\n\n* fix(ai): enforce a shared run deadline across provider stages"
    },
    {
      "commit": "52e8f9c48722139499f1e3d7dd4ddf0dbdf9e665",
      "tree": "1fa0d70a519317340e3c24afc4ae741f98af3a60",
      "parents": [
        "c1f9b2e3eb4479936efd12ffac9b17f625b94d06"
      ],
      "author": {
        "name": "andy_chan",
        "email": "100347913+project-nju-learner@users.noreply.github.com",
        "time": "Mon Oct 26 16:33:15 2026 +0800"
      },
      "committer": {
        "name": "GitHub",
        "email": "noreply@github.com",
        "time": "Mon Oct 26 16:33:15 2026 +0800"
      },
      "message": "fix(settings): isolate data source modal connection tests (#6926)\n\n* fix(settings): isolate data source modal connection tests"
    },
    {
      "commit": "c1f9b2e3eb4479936efd12ffac9b17f625b94d06",
      "tree": "b62d17f25f0c1af6901a450849d93efcd5ac85b0",
      "parents": [
        "6ddbc6db5d37d727758c4c286d818eb87b3011e8"
      ],
      "author": {
        "name": "pilichoumao",
        "email": "99873428+pilichoumao@users.noreply.github.com",
        "time": "Mon Oct 26 16:32:53 2026 +0800"
      },
      "committer": {
        "name": "GitHub",
        "email": "noreply@github.com",
        "time": "Mon Oct 26 16:32:53 2026 +0800"
      },
      "message": "fix(alert): require numeric zero in DingTalk responses (#6924)\n\n* fix(alert): require numeric zero in DingTalk responses"
    },
    {
      "commit": "6ddbc6db5d37d727758c4c286d818eb87b3011e8",
      "tree": "6b5fc8f3adf3c5d299c228a00aeb72167d04604a",
      "parents": [
        "abdc399f7e722513570794ebd764adcf99c3311b"
      ],
      "author": {
        "name": "pilichoumao",
        "email": "99873428+pilichoumao@users.noreply.github.com",
        "time": "Mon Oct 26 16:32:30 2026 +0800"
      },
      "committer": {
        "name": "GitHub",
        "email": "noreply@github.com",
        "time": "Mon Oct 26 16:32:30 2026 +0800"
      },
      "message": "fix(alert): preserve comments in email recipient lists (#6922)\n\n* fix(alert): preserve comments in email recipient lists"
    },
    {
      "commit": "abdc399f7e722513570794ebd764adcf99c3311b",
      "tree": "957226f8248ffcf5f500ab1cea559bdc14c67f54",
      "parents": [
        "b1561bde45204578032f84dbb4cfd550c1827b0d"
      ],
      "author": {
        "name": "dxz0069",
        "email": "165159648+dxz0069@users.noreply.github.com",
        "time": "Mon Oct 26 16:28:56 2026 +0800"
      },
      "committer": {
        "name": "GitHub",
        "email": "noreply@github.com",
        "time": "Mon Oct 26 16:28:56 2026 +0800"
      },
      "message": "fix(metrics): accept Prometheus decimal and exponent step forms (#7055)\n\nThe step syntax gate required digits on both sides of a decimal point\nand excluded scientific notation, so .5, 1., +1, 1e1, 5E-1 and 1e0 were\nrejected as invalid before dispatch even though Prometheus 3.5.0 and the\nStudio PrometheusMetricsSource accept them. The gate now matches the\nPrometheus float grammar - sign, decimal point on either side, exponent\n- which BigDecimal\u0027s own grammar accepts, so the step text parses and is\ndispatched unchanged. Positivity and the 11,000-point sample budget are\nunchanged downstream checks on the parsed value: a 20-second range still\nrejects 0.001, .001 and 1e-3.\n\nFixes #6948"
    },
    {
      "commit": "b1561bde45204578032f84dbb4cfd550c1827b0d",
      "tree": "4da562edd7a6dddd6db9e0d52454faf58dbc1ec9",
      "parents": [
        "c2c7d92860c4ee90a114ce3d9e3e7f340171cf06"
      ],
      "author": {
        "name": "wizcraft_kris",
        "email": "99409434+Kris20030907@users.noreply.github.com",
        "time": "Mon Oct 26 16:28:43 2026 +0800"
      },
      "committer": {
        "name": "GitHub",
        "email": "noreply@github.com",
        "time": "Mon Oct 26 16:28:43 2026 +0800"
      },
      "message": "fix(ai): stamp gmt_modified when reporting run speed (#7048)"
    },
    {
      "commit": "c2c7d92860c4ee90a114ce3d9e3e7f340171cf06",
      "tree": "a365f1407e0690cb6f6d4226b1e21ca4571752a8",
      "parents": [
        "c7a10172acd530654b38c491bc2e942e1bb9d6a3"
      ],
      "author": {
        "name": "九斤",
        "email": "51957749+JerseyYee@users.noreply.github.com",
        "time": "Mon Oct 26 16:28:28 2026 +0800"
      },
      "committer": {
        "name": "GitHub",
        "email": "noreply@github.com",
        "time": "Mon Oct 26 16:28:28 2026 +0800"
      },
      "message": "fix(ai): reject non-positive reset timestamp before any broker call (#6893)\n\nFixes #5984\n\nrmq.group.reset_offset only rejected a null timestamp; 0 or negative values passed preview and execute. The broker resolves such a timestamp to each queue minimum offset, rewinding every queue (mass replay) via the AI tool API, MCP tools/call and rmqctl. Reject timestamp \u003c\u003d 0 in the shared handler with the REST contract message (400), guarding both preview and execute before any broker call. Regression tests cover 0/-1 on both paths."
    },
    {
      "commit": "c7a10172acd530654b38c491bc2e942e1bb9d6a3",
      "tree": "a8d376b87877713754be099747326e8edee3b071",
      "parents": [
        "fe785b81044dd7dd409d733448accd8267de3ecf"
      ],
      "author": {
        "name": "lizhimins",
        "email": "707364882@qq.com",
        "time": "Mon Oct 26 16:03:38 2026 +0800"
      },
      "committer": {
        "name": "GitHub",
        "email": "noreply@github.com",
        "time": "Mon Oct 26 16:03:38 2026 +0800"
      },
      "message": "feat(web,server): AI conversation upgrades and console density/label fixes (#7327)\n\n* feat(web,server): AI conversation upgrades and console density/label fixes\n\n- assistant bubbles render as a fixed-width column so streaming no longer\n  resizes them; tool cards collapse to one line with inline input/output\n  switches and a live stopwatch that turns warning/error at 5/10 minutes\n- follow-up suggestions: one small completion over the last question and\n  answer, rendered as clickable chips after a run finishes\n- messages typed during a run queue locally and drain in order; a\n  conversation minimap navigates long transcripts; the context meter uses\n  provider-reported usage instead of a transcript estimate\n- structured tool output: row-list JSON and CSV render as tables, other\n  JSON pretty-printed and colour-coded, raw text one click away\n- reset the persisted agent engine to claude-code: a stale qoder\n  preference failed every run with a bare failure tag\n- row density: alert deliveries and alert events keep one line per row;\n  alert events becomes a multi-select table with batch acknowledge and\n  expand-row details\n- create-rule modal compaction (help text in label tooltips, template\n  editor and preview in a collapsed panel); certificate k8s IDs truncate\n  with tooltip and copy; a flush disk type change warns that it only\n  applies after a Broker restart and asks for a second confirmation\n- health diagnosis summary labels name their units and calibers\n- layout: the sider menu scrolls on short viewports; below md the\n  navigation moves into a drawer with a compact top bar\n\n* fix(ai): align the follow-up surface with the contract and style rules\n\nReview fixes on top of the follow-up endpoint and the console density round:\n\n- document POST /api/ai/conversations/:id/followups in docs/api-spec.md (row 121\n  plus a 15.14 section), including the empty-array-is-success contract and the\n  bounds the service enforces\n- the controller javadoc still claimed eleven endpoints (nine JSON + two SSE);\n  the surface is now thirteen (eleven JSON + two SSE)\n- ask the model to reply in the language of the last answer, so the chips are not\n  Chinese-only in an English console\n- abbreviate the question as well as the answer before it goes into the prompt: a\n  pasted log used to enter the template unbounded\n- the alert level count in systemAlerts renders at 14px like every other label;\n  the 12px was the only sub-14px text this branch added"
    },
    {
      "commit": "fe785b81044dd7dd409d733448accd8267de3ecf",
      "tree": "ad68cd98e1dbc8aec67f5d0aaa07531eed8efa80",
      "parents": [
        "6dd02d2759e7c4bb6b7ba34f6a768628f03377f1"
      ],
      "author": {
        "name": "lizhimins",
        "email": "707364882@qq.com",
        "time": "Mon Oct 26 15:06:31 2026 +0800"
      },
      "committer": {
        "name": "GitHub",
        "email": "noreply@github.com",
        "time": "Mon Oct 26 15:06:31 2026 +0800"
      },
      "message": "feat(web): fold the resource model page into the create-form rule modal (#7355)\n\nDrop the /studio/resource-model route, its menu entry and the page component: the\nownership topology and the four rules now render inside the question-mark modal on\nthe create instance, topic and group forms, widened to 860px with a scrollable body.\nThe modal title becomes the rule name and the tooltip reuses the aria label so the\ntwo stay distinguishable to assistive tech.\n\nThe modal now ends with the four ownership rules. The three-outcome list is dropped\nbecause rule 1 already says a foreign owner is reported at creation, so the list only\nrestated it."
    },
    {
      "commit": "6dd02d2759e7c4bb6b7ba34f6a768628f03377f1",
      "tree": "94637d017c6901d1e9707c30c2b18ba29d3160ee",
      "parents": [
        "ab778e7c12855c4d8a49186320d176fd3e8c1170"
      ],
      "author": {
        "name": "lizhimins",
        "email": "707364882@qq.com",
        "time": "Mon Oct 26 15:05:15 2026 +0800"
      },
      "committer": {
        "name": "GitHub",
        "email": "noreply@github.com",
        "time": "Mon Oct 26 15:05:15 2026 +0800"
      },
      "message": "feat(web): surface the naming rule on the create forms and document the model (#7354)\n\nAdd a nameserver selector and column to the instance page, a resource model page\nwith the ownership topology, and a question-mark help modal on the create\ninstance, topic and group forms that spells out the three creation outcomes.\nThe creation-decision flow diagram is deliberately left out of the page: it read\nas a backend implementation detail, so the rule now lives where creation happens."
    },
    {
      "commit": "ab778e7c12855c4d8a49186320d176fd3e8c1170",
      "tree": "6797719ac75e31662859168b7e93fb8d725ba200",
      "parents": [
        "127c47b06a268cc74224d5f34c56ec7c36ac0d55"
      ],
      "author": {
        "name": "lizhimins",
        "email": "707364882@qq.com",
        "time": "Mon Oct 26 14:59:04 2026 +0800"
      },
      "committer": {
        "name": "GitHub",
        "email": "noreply@github.com",
        "time": "Mon Oct 26 14:59:04 2026 +0800"
      },
      "message": "feat(instance): scope topic and group uniqueness to the nameserver domain (#7353)\n\nBind each APACHE instance to a nameserver registry entry at creation and derive a\nNOT NULL ownership domain from it (ns:\u003cnameserver_id\u003e, cloud:\u003ccloudInstanceId\u003e),\nso a topic or group name is unique per (domain, broker cluster) instead of per\ndeployment. The ownership guard becomes slot based: same name on another cluster\nof the domain coexists, the owning instance re-creates as an update, and a foreign\nowner is reported with the create rejected.\n\nRewrite docs/studio-instance-model-spec.md to the domain wording: the spec still\nclaimed deployment-wide global uniqueness and a name-only unique key, which is what\nthis change replaces."
    },
    {
      "commit": "127c47b06a268cc74224d5f34c56ec7c36ac0d55",
      "tree": "a17cca6681595b0b9f9082794e65c87aff228894",
      "parents": [
        "feb96cbeaf662148cfe51b62d9336d8e7c1a3c2c"
      ],
      "author": {
        "name": "lizhimins",
        "email": "707364882@qq.com",
        "time": "Mon Oct 19 21:11:05 2026 +0800"
      },
      "committer": {
        "name": "GitHub",
        "email": "noreply@github.com",
        "time": "Mon Oct 19 21:11:05 2026 +0800"
      },
      "message": "test(acl): give the UTC wire-format fixture its required resourceType (#7081)\n\n#6686 added a listAclRules fixture that omits resourceType, which AclRule\nrequires. vitest transpiles without type checking so the suite stayed green,\nbut `tsc -b` (the frontend build gate) failed on the merged trunk."
    },
    {
      "commit": "feb96cbeaf662148cfe51b62d9336d8e7c1a3c2c",
      "tree": "d6e209679ea8624b08075645b847fd4b9dcaf013",
      "parents": [
        "97f668006bb0b8d6bb8e1f6a6fc661b961ad28b2"
      ],
      "author": {
        "name": "pilichoumao",
        "email": "99873428+pilichoumao@users.noreply.github.com",
        "time": "Mon Oct 19 20:59:00 2026 +0800"
      },
      "committer": {
        "name": "GitHub",
        "email": "noreply@github.com",
        "time": "Mon Oct 19 20:59:00 2026 +0800"
      },
      "message": "feat(ai): add the rmq.litetopic.extend_ttl mutation tool (#5116)\n\nVerified against `rocketmq-studio` at 97f668006 before merging: the defect is still present on the\ncurrent trunk and the change merges cleanly.\n\nMerged as-is (squash); no maintainer edits were needed.\n\nLocally verified: targeted suites green on the integration branch; a full backend and frontend\nbuild plus test pass runs on the trunk once this batch lands."
    },
    {
      "commit": "97f668006bb0b8d6bb8e1f6a6fc661b961ad28b2",
      "tree": "a56b20467b729c92412e2646d56cd03d55ca618a",
      "parents": [
        "a4dee573c2fef38ecd719b38dcfc532ebd32553d"
      ],
      "author": {
        "name": "Loyal-Young",
        "email": "hjy1713252343@gmail.com",
        "time": "Mon Oct 19 20:52:59 2026 +0800"
      },
      "committer": {
        "name": "GitHub",
        "email": "noreply@github.com",
        "time": "Mon Oct 19 20:52:59 2026 +0800"
      },
      "message": "fix(export): date CSV filenames in the operator timezone (#6898)\n\nVerified against `rocketmq-studio` at a4dee573c before merging: the defect is still present on the\ncurrent trunk and the change merges cleanly.\n\nMerged as-is (squash); no maintainer edits were needed.\n\nLocally verified: targeted suites green on the integration branch; a full backend and frontend\nbuild plus test pass runs on the trunk once this batch lands."
    },
    {
      "commit": "a4dee573c2fef38ecd719b38dcfc532ebd32553d",
      "tree": "275ef866a76006bd2974c525eb31d29262d3fd48",
      "parents": [
        "62d0ad92e931de46a2fc944ae5d970e6fc3d7311"
      ],
      "author": {
        "name": "烤化の初雪",
        "email": "yangzihao_41@outlook.com",
        "time": "Mon Oct 19 20:52:30 2026 +0800"
      },
      "committer": {
        "name": "GitHub",
        "email": "noreply@github.com",
        "time": "Mon Oct 19 20:52:30 2026 +0800"
      },
      "message": "fix(audit): record a cloud-accepted direct consume as a success (#5119)\n\nVerified against `rocketmq-studio` at 62d0ad92e before merging: the defect is still present on the\ncurrent trunk and the change merges cleanly.\n\nMaintainer follow-ups on top of the contributor commit:\n- fix(audit): grade a cloud-accepted direct consume as PARTIAL\n\nEach follow-up commit message records the review finding it addresses.\n\nLocally verified: targeted suites green on the integration branch; a full backend and frontend\nbuild plus test pass runs on the trunk once this batch lands."
    },
    {
      "commit": "62d0ad92e931de46a2fc944ae5d970e6fc3d7311",
      "tree": "5c95597e2e5e3217f872cda04813e5c896633f63",
      "parents": [
        "e1577bd3106603e1ad1fe724a0cbdd0b3ad61257"
      ],
      "author": {
        "name": "0",
        "email": "1939455790@qq.com",
        "time": "Mon Oct 19 20:52:10 2026 +0800"
      },
      "committer": {
        "name": "GitHub",
        "email": "noreply@github.com",
        "time": "Mon Oct 19 20:52:10 2026 +0800"
      },
      "message": "fix(alerts): count 24h triggers from the UTC stamp (#5723)\n\nVerified against `rocketmq-studio` at e1577bd31 before merging: the defect is still present on the\ncurrent trunk and the change merges cleanly.\n\nMerged as-is (squash); no maintainer edits were needed.\n\nLocally verified: targeted suites green on the integration branch; a full backend and frontend\nbuild plus test pass runs on the trunk once this batch lands."
    },
    {
      "commit": "e1577bd3106603e1ad1fe724a0cbdd0b3ad61257",
      "tree": "c7f6f9178f5746368613cf6434a0d380d8502e6e",
      "parents": [
        "3d1641bad17ddd4c7a5d9fff7252dfc0028d7bf2"
      ],
      "author": {
        "name": "0",
        "email": "1939455790@qq.com",
        "time": "Mon Oct 19 20:51:51 2026 +0800"
      },
      "committer": {
        "name": "GitHub",
        "email": "noreply@github.com",
        "time": "Mon Oct 19 20:51:51 2026 +0800"
      },
      "message": "fix(ai): page the attach replay through the whole backlog (#6389)\n\nVerified against `rocketmq-studio` at 3d1641bad before merging: the defect is still present on the\ncurrent trunk and the change merges cleanly.\n\nMaintainer follow-ups on top of the contributor commit:\n- docs(ai): promise the lowest limit rows from the seq window\n\nEach follow-up commit message records the review finding it addresses.\n\nLocally verified: targeted suites green on the integration branch; a full backend and frontend\nbuild plus test pass runs on the trunk once this batch lands."
    },
    {
      "commit": "3d1641bad17ddd4c7a5d9fff7252dfc0028d7bf2",
      "tree": "f5fa53f07d2e66f5658c001da2028d18b3c94250",
      "parents": [
        "2cb82a81df9d7c8f8d0d599563687b8fcc8ec8fd"
      ],
      "author": {
        "name": "dxz0069",
        "email": "165159648+dxz0069@users.noreply.github.com",
        "time": "Mon Oct 19 20:09:39 2026 +0800"
      },
      "committer": {
        "name": "GitHub",
        "email": "noreply@github.com",
        "time": "Mon Oct 19 20:09:39 2026 +0800"
      },
      "message": "fix(audit,acl): serialize page timestamps on the UTC wire (#6686)\n\nVerified against `rocketmq-studio` at 2cb82a81d before merging: the defect is still present on the\ncurrent trunk and the change merges cleanly.\n\nMerged as-is (squash); no maintainer edits were needed.\n\nLocally verified: targeted suites green on the integration branch; a full backend and frontend\nbuild plus test pass runs on the trunk once this batch lands."
    },
    {
      "commit": "2cb82a81df9d7c8f8d0d599563687b8fcc8ec8fd",
      "tree": "7839d64eca15be7923e6bcc75f761e2701a8452d",
      "parents": [
        "bd1dafabfcb38b2ee4d84db3d3dd4d2e8eb46bdb"
      ],
      "author": {
        "name": "Qingyuan Wang",
        "email": "146524024+NUAAwqy@users.noreply.github.com",
        "time": "Mon Oct 19 20:09:11 2026 +0800"
      },
      "committer": {
        "name": "GitHub",
        "email": "noreply@github.com",
        "time": "Mon Oct 19 20:09:11 2026 +0800"
      },
      "message": "fix(persistence): widen username columns on existing databases (#6915)\n\nVerified against `rocketmq-studio` at bd1dafabf before merging: the defect is still present on the\ncurrent trunk and the change merges cleanly.\n\nMaintainer follow-ups on top of the contributor commit:\n- fix(persistence): report an unreadable column width as an unconfirmed widening\n\nEach follow-up commit message records the review finding it addresses.\n\nLocally verified: targeted suites green on the integration branch; a full backend and frontend\nbuild plus test pass runs on the trunk once this batch lands."
    },
    {
      "commit": "bd1dafabfcb38b2ee4d84db3d3dd4d2e8eb46bdb",
      "tree": "2a5afdc9b7f782bfdb03b3ce5538917ecafb2d10",
      "parents": [
        "ecd7d62adee6b7e3722b718b7f5370ed347cd8e4"
      ],
      "author": {
        "name": "coder999o",
        "email": "soft_99@126.com",
        "time": "Mon Oct 19 20:08:42 2026 +0800"
      },
      "committer": {
        "name": "GitHub",
        "email": "noreply@github.com",
        "time": "Mon Oct 19 20:08:42 2026 +0800"
      },
      "message": "fix(rmqctl): reject embedded credentials in context server URLs (#6689)\n\nVerified against `rocketmq-studio` at ecd7d62ad before merging: the defect is still present on the\ncurrent trunk and the change merges cleanly.\n\nMaintainer follow-ups on top of the contributor commit:\n- fix(rmqctl): reject a query or fragment in --server before it is written\n\nEach follow-up commit message records the review finding it addresses.\n\nLocally verified: targeted suites green on the integration branch; a full backend and frontend\nbuild plus test pass runs on the trunk once this batch lands."
    },
    {
      "commit": "ecd7d62adee6b7e3722b718b7f5370ed347cd8e4",
      "tree": "b9852f60553752d4c9950dea5dfc731f45b0fa40",
      "parents": [
        "056af345c9df7158e611a1ae6337ae55fb164faf"
      ],
      "author": {
        "name": "Xiao Yang",
        "email": "yangx_soft@163.com",
        "time": "Mon Oct 19 20:08:20 2026 +0800"
      },
      "committer": {
        "name": "GitHub",
        "email": "noreply@github.com",
        "time": "Mon Oct 19 20:08:20 2026 +0800"
      },
      "message": "fix(alert): reject native rules without a supported operator (#6642)\n\nVerified against `rocketmq-studio` at 056af345c before merging: the defect is still present on the\ncurrent trunk and the change merges cleanly.\n\nMaintainer follow-ups on top of the contributor commit:\n- fix(alert): judge the trimmed native operator and pin the whitelist to the evaluator\n\nEach follow-up commit message records the review finding it addresses.\n\nLocally verified: targeted suites green on the integration branch; a full backend and frontend\nbuild plus test pass runs on the trunk once this batch lands."
    },
    {
      "commit": "056af345c9df7158e611a1ae6337ae55fb164faf",
      "tree": "6b2b7ca71912098e8d86adf0c757ef6438d4d70b",
      "parents": [
        "96af74093ed9f42c76a7d2211fae1aa7314b5548"
      ],
      "author": {
        "name": "Apulupie",
        "email": "167634780+Frun1na@users.noreply.github.com",
        "time": "Mon Oct 19 20:07:04 2026 +0800"
      },
      "committer": {
        "name": "GitHub",
        "email": "noreply@github.com",
        "time": "Mon Oct 19 20:07:04 2026 +0800"
      },
      "message": "fix(web): render panels on theme tokens so dark mode stays readable (#5662)\n\nVerified against `rocketmq-studio` at 96af74093 before merging: the defect is still present on the\ncurrent trunk and the change merges cleanly.\n\nMaintainer follow-ups on top of the contributor commit:\n- fix(web): cover the panels the dark-mode token pass missed\n\nEach follow-up commit message records the review finding it addresses.\n\nLocally verified: targeted suites green on the integration branch; a full backend and frontend\nbuild plus test pass runs on the trunk once this batch lands."
    },
    {
      "commit": "96af74093ed9f42c76a7d2211fae1aa7314b5548",
      "tree": "1eb20ef1eb8125c9d3f443021c17377cddf0d62e",
      "parents": [
        "420a506c225fa115aeb922d24f9a1df4330687b8"
      ],
      "author": {
        "name": "Apulupie",
        "email": "167634780+Frun1na@users.noreply.github.com",
        "time": "Mon Oct 19 20:01:40 2026 +0800"
      },
      "committer": {
        "name": "GitHub",
        "email": "noreply@github.com",
        "time": "Mon Oct 19 20:01:40 2026 +0800"
      },
      "message": "docs: document the auth status and password endpoints (#5686)\n\nVerified against `rocketmq-studio` at 420a506c2 before merging: the defect is still present on the\ncurrent trunk and the change merges cleanly.\n\nMerged as-is (squash); no maintainer edits were needed.\n\nLocally verified: targeted suites green on the integration branch; a full backend and frontend\nbuild plus test pass runs on the trunk once this batch lands."
    },
    {
      "commit": "420a506c225fa115aeb922d24f9a1df4330687b8",
      "tree": "bd27f00e3cb44248e78741e043124c46d5e803e3",
      "parents": [
        "3eff1f0cae75cad995dc3c228e4ae71922d652c2"
      ],
      "author": {
        "name": "Apulupie",
        "email": "167634780+Frun1na@users.noreply.github.com",
        "time": "Mon Oct 19 20:00:08 2026 +0800"
      },
      "committer": {
        "name": "GitHub",
        "email": "noreply@github.com",
        "time": "Mon Oct 19 20:00:08 2026 +0800"
      },
      "message": "docs: document the LiteTopic endpoints (#5684)\n\nVerified against `rocketmq-studio` at 3eff1f0ca before merging: the defect is still present on the\ncurrent trunk and the change merges cleanly.\n\nMerged as-is (squash); no maintainer edits were needed.\n\nLocally verified: targeted suites green on the integration branch; a full backend and frontend\nbuild plus test pass runs on the trunk once this batch lands."
    },
    {
      "commit": "3eff1f0cae75cad995dc3c228e4ae71922d652c2",
      "tree": "afdd8648b2f33ea66c34a3abc227172f3b41aa1d",
      "parents": [
        "e8fc8b037a738905c6675f00d7c5af94f5a08550"
      ],
      "author": {
        "name": "Apulupie",
        "email": "167634780+Frun1na@users.noreply.github.com",
        "time": "Mon Oct 19 19:59:37 2026 +0800"
      },
      "committer": {
        "name": "GitHub",
        "email": "noreply@github.com",
        "time": "Mon Oct 19 19:59:37 2026 +0800"
      },
      "message": "fix(web): keep an open delivery drawer in step with a refreshed row (#5665)\n\nVerified against `rocketmq-studio` at e8fc8b037 before merging: the defect is still present on the\ncurrent trunk and the change merges cleanly.\n\nMaintainer follow-ups on top of the contributor commit:\n- fix(web): patch the page row a retried delivery dialog reads\n\nEach follow-up commit message records the review finding it addresses.\n\nLocally verified: targeted suites green on the integration branch; a full backend and frontend\nbuild plus test pass runs on the trunk once this batch lands."
    },
    {
      "commit": "e8fc8b037a738905c6675f00d7c5af94f5a08550",
      "tree": "adcd4697c3980d5378fa45821d848e28cdcf255a",
      "parents": [
        "3bcd14d4552b1fa9f3a9142730f2c34409ed181a"
      ],
      "author": {
        "name": "Apulupie",
        "email": "167634780+Frun1na@users.noreply.github.com",
        "time": "Mon Oct 19 19:57:35 2026 +0800"
      },
      "committer": {
        "name": "GitHub",
        "email": "noreply@github.com",
        "time": "Mon Oct 19 19:57:35 2026 +0800"
      },
      "message": "fix(web): warn on partially matched DLQ selected resends instead of success (#4912)\n\nVerified against `rocketmq-studio` at 3bcd14d45 before merging: the defect is still present on the\ncurrent trunk and the change merges cleanly.\n\nMaintainer follow-ups on top of the contributor commit:\n- fix(dlq): report unmatched and unscanned queues on the failed selected resend too\n\nEach follow-up commit message records the review finding it addresses.\n\nLocally verified: targeted suites green on the integration branch; a full backend and frontend\nbuild plus test pass runs on the trunk once this batch lands."
    },
    {
      "commit": "3bcd14d4552b1fa9f3a9142730f2c34409ed181a",
      "tree": "edb6770740b2d23efcd6a11a6263702fdc0ae4c0",
      "parents": [
        "c702d301056c8aa3718a6f463fa43dcd416fd5a0"
      ],
      "author": {
        "name": "Apulupie",
        "email": "167634780+Frun1na@users.noreply.github.com",
        "time": "Mon Oct 19 19:57:05 2026 +0800"
      },
      "committer": {
        "name": "GitHub",
        "email": "noreply@github.com",
        "time": "Mon Oct 19 19:57:05 2026 +0800"
      },
      "message": "fix(certs): localize the K8s certificate page (#4619)\n\nVerified against `rocketmq-studio` at c702d3010 before merging: the defect is still present on the\ncurrent trunk and the change merges cleanly.\n\nMaintainer follow-ups on top of the contributor commit:\n- fix(certs): keep the localized load error without re-fetching on language change\n\nEach follow-up commit message records the review finding it addresses.\n\nLocally verified: targeted suites green on the integration branch; a full backend and frontend\nbuild plus test pass runs on the trunk once this batch lands."
    },
    {
      "commit": "c702d301056c8aa3718a6f463fa43dcd416fd5a0",
      "tree": "8485a5533592cde588acb66658ca9735a70705fc",
      "parents": [
        "674e7017d040652da0aeb1fc36976a82a199087d"
      ],
      "author": {
        "name": "Apulupie",
        "email": "167634780+Frun1na@users.noreply.github.com",
        "time": "Mon Oct 19 19:56:07 2026 +0800"
      },
      "committer": {
        "name": "GitHub",
        "email": "noreply@github.com",
        "time": "Mon Oct 19 19:56:07 2026 +0800"
      },
      "message": "fix(dlq): scope the group search to the selected instance (#4577)\n\nVerified against `rocketmq-studio` at 674e7017d before merging: the defect is still present on the\ncurrent trunk and the change merges cleanly.\n\nMaintainer follow-ups on top of the contributor commit:\n- fix(dlq): clear the search term only when the instance actually changes\n\nEach follow-up commit message records the review finding it addresses.\n\nLocally verified: targeted suites green on the integration branch; a full backend and frontend\nbuild plus test pass runs on the trunk once this batch lands."
    },
    {
      "commit": "674e7017d040652da0aeb1fc36976a82a199087d",
      "tree": "4ca3f1ac720841d4e65299aa7b3b90a4fe5f6c5a",
      "parents": [
        "f963be9c4513b1b32ef5427f2cd64d220cd8342a"
      ],
      "author": {
        "name": "0",
        "email": "1939455790@qq.com",
        "time": "Mon Oct 19 19:54:24 2026 +0800"
      },
      "committer": {
        "name": "GitHub",
        "email": "noreply@github.com",
        "time": "Mon Oct 19 19:54:24 2026 +0800"
      },
      "message": "fix(ai): stop the retention purge deleting busy chats (#5810)\n\nVerified against `rocketmq-studio` at fbdc5f22b before merging: the defect is still present on the\ncurrent trunk and the change merges cleanly.\n\nMaintainer follow-ups on top of the contributor commit:\n- fix(ai): keep the retention purge moving past the conversations it skips\n\nEach follow-up commit message records the review finding it addresses.\n\nLocally verified: targeted suites green on the integration branch; a full backend and frontend\nbuild plus test pass runs on the trunk once this batch lands."
    },
    {
      "commit": "f963be9c4513b1b32ef5427f2cd64d220cd8342a",
      "tree": "efd44238eae0ca4f2e43363d91847cb85aff208b",
      "parents": [
        "32345ec18e66e05d35c3b9a99e8768b1b229c712"
      ],
      "author": {
        "name": "0",
        "email": "1939455790@qq.com",
        "time": "Mon Oct 19 19:54:09 2026 +0800"
      },
      "committer": {
        "name": "GitHub",
        "email": "noreply@github.com",
        "time": "Mon Oct 19 19:54:09 2026 +0800"
      },
      "message": "fix(ai): stop markdown repair breaking headings (#5730)\n\nVerified against `rocketmq-studio` at fbdc5f22b before merging: the defect is still present on the\ncurrent trunk and the change merges cleanly.\n\nMaintainer follow-ups on top of the contributor commit:\n- fix(ai): guard the heading and bullet repairs against well-formed markers\n\nEach follow-up commit message records the review finding it addresses.\n\nLocally verified: targeted suites green on the integration branch; a full backend and frontend\nbuild plus test pass runs on the trunk once this batch lands."
    },
    {
      "commit": "32345ec18e66e05d35c3b9a99e8768b1b229c712",
      "tree": "f830dd9731ba85d17a03bb4c0b261194fdf67563",
      "parents": [
        "fbdc5f22b8626ebf5dbdb8fc18d52a820a1e2a0b"
      ],
      "author": {
        "name": "0",
        "email": "1939455790@qq.com",
        "time": "Mon Oct 19 19:53:54 2026 +0800"
      },
      "committer": {
        "name": "GitHub",
        "email": "noreply@github.com",
        "time": "Mon Oct 19 19:53:54 2026 +0800"
      },
      "message": "fix(web): render a placeholder for an unknown instance protocol (#5729)\n\nVerified against `rocketmq-studio` at fbdc5f22b before merging: the defect is still present on the\ncurrent trunk and the change merges cleanly.\n\nMaintainer follow-ups on top of the contributor commit:\n- test(consumer): align the unknown-protocol placeholder with the page convention\n\nEach follow-up commit message records the review finding it addresses.\n\nLocally verified: targeted suites green on the integration branch; a full backend and frontend\nbuild plus test pass runs on the trunk once this batch lands."
    },
    {
      "commit": "fbdc5f22b8626ebf5dbdb8fc18d52a820a1e2a0b",
      "tree": "8c5b7cea05b7228f95097db0a8e2d12be780da9c",
      "parents": [
        "70be77c5cbc9015fe9c866c8d1bbd3d087d9de32"
      ],
      "author": {
        "name": "Loyal-Young",
        "email": "hjy1713252343@gmail.com",
        "time": "Mon Oct 19 19:47:22 2026 +0800"
      },
      "committer": {
        "name": "GitHub",
        "email": "noreply@github.com",
        "time": "Mon Oct 19 19:47:22 2026 +0800"
      },
      "message": "fix(ai): scope prompt templates to the operator, keep replayed durations, report failed overview reads (#6707)\n\nBatched integration of @Loyal-Young\u0027s AI, a11y, user-management and docs fixes. This branch carries\nfive PRs\u0027 worth of changes; the individual PRs are closed as folded into this one.\n\n- #6707 — scope custom prompt templates to the operator, so two accounts sharing a browser no longer\n  see each other\u0027s templates.\n- #6469 — retain the recorded run duration when a terminal event is replayed.\n- #6908 — expose collapsed tag labels on focus.\n- #6902 — report failed session-overview reads instead of rendering them as empty.\n- #6911 — document how the demo clients reach the NameServer.\n\nMaintainer fixes on top: `promptTemplates` lines rewrapped to the configured print width and the new\nmodal case moved out of the \"denied storage\" describe; the forwarding-only one-arg `replay` overload\ndropped (zero call sites, and keeping it would keep producing `durationMs\u003dnull` frames); two\nsession-overview retry lines rewrapped.\n\nThe `AiRunService` cherry-pick conflicted with #5753 / #5757 / #5758 / #5727, which landed on the\ntrunk today; resolved as a union — the observer-before-replay ordering, the attach-session close on a\nfailed replay and the buffered-frame drain under the send lock are all intact, and this branch only\nchanges the single `projector.replay(...)` call site plus the projector signature.\n\nVerified locally against `rocketmq-studio`: `AiRunServiceTest` 32/32 and `AgentEventProjectorTest`\n40/40 green, the four affected web suites 43/43 green, `npm run build` and the web license check pass,\neslint clean."
    }
  ],
  "next": "70be77c5cbc9015fe9c866c8d1bbd3d087d9de32"
}
