From 7263ac627d9732f37083a18eaedecfb705b0fdba Mon Sep 17 00:00:00 2001 From: James Maes Date: Mon, 12 Oct 2026 12:49:33 -0500 Subject: [PATCH 01/27] Bump version to 0.1.0-RC.1 [skip ci] The orb published 0.1.0-RC.1 but could not push this commit (the CircleCI deploy key is read only). Co-Authored-By: Claude Opus 5.5 --- pom.xml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/pom.xml b/pom.xml index 6e823ea..adb2610 100644 --- a/pom.xml +++ b/pom.xml @@ -20,7 +20,7 @@ - 0.1.0-SNAPSHOT + 0.1.0-RC.1 21 21 UTF-8 -- 2.54.0 From ff67c6f7cc3bee75373c3e18667229c5e47b2260 Mon Sep 17 00:00:00 2001 From: James Maes Date: Mon, 12 Oct 2026 15:56:59 -0500 Subject: [PATCH 02/27] Set version to 0.1.0-RC.2 and publish only GA tags [skip ci] 0.1.0-RC.2 is already on Maven Central (an RC tag build republished the previous code), so the next release-branch build must publish the following RC. RC tags no longer trigger a publish (/^v[0-9]+\.[0-9]+\.[0-9]+$/ only). Co-Authored-By: Claude Opus 5.5 --- .circleci/config.yml | 2 +- pom.xml | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/.circleci/config.yml b/.circleci/config.yml index 65ab99c..56b977f 100644 --- a/.circleci/config.yml +++ b/.circleci/config.yml @@ -57,7 +57,7 @@ workflows: branches: ignore: /.*/ tags: - only: [/v.*/] + only: [/^v[0-9]+\.[0-9]+\.[0-9]+$/] publish_hotfix_release: jobs: diff --git a/pom.xml b/pom.xml index adb2610..e567c67 100644 --- a/pom.xml +++ b/pom.xml @@ -20,7 +20,7 @@ - 0.1.0-RC.1 + 0.1.0-RC.2 21 21 UTF-8 -- 2.54.0 From 67f78fd660221285f572562e3485cff976c32664 Mon Sep 17 00:00:00 2001 From: James Maes Date: Mon, 12 Oct 2026 16:01:54 -0500 Subject: [PATCH 03/27] ci: fix the GA tag filter YAML (no brackets in the flow sequence) [skip ci] Co-Authored-By: Claude Opus 5.5 --- .circleci/config.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.circleci/config.yml b/.circleci/config.yml index 56b977f..2b9734e 100644 --- a/.circleci/config.yml +++ b/.circleci/config.yml @@ -57,7 +57,7 @@ workflows: branches: ignore: /.*/ tags: - only: [/^v[0-9]+\.[0-9]+\.[0-9]+$/] + only: [/^v\d+\.\d+\.\d+$/] publish_hotfix_release: jobs: -- 2.54.0 From a8c5f586eea9288e0679cfa9a0fa53c6d7600c00 Mon Sep 17 00:00:00 2001 From: CircleCI Date: Mon, 12 Oct 2026 21:22:35 +0000 Subject: [PATCH 04/27] Bump version to 0.1.0-RC.3 [skip ci] --- pom.xml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/pom.xml b/pom.xml index e567c67..3b74ecd 100644 --- a/pom.xml +++ b/pom.xml @@ -20,7 +20,7 @@ - 0.1.0-RC.2 + 0.1.0-RC.3 21 21 UTF-8 -- 2.54.0 From d0ad2aa0fa41db3894d5a47a4e6278852c0c6aae Mon Sep 17 00:00:00 2001 From: CircleCI Date: Tue, 13 Oct 2026 15:24:59 +0000 Subject: [PATCH 05/27] Bump version to 0.1.0-RC.4 [skip ci] --- pom.xml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/pom.xml b/pom.xml index 3b74ecd..c7e2c3b 100644 --- a/pom.xml +++ b/pom.xml @@ -20,7 +20,7 @@ - 0.1.0-RC.3 + 0.1.0-RC.4 21 21 UTF-8 -- 2.54.0 From ac6ec75deb4a4c7f0a8df496aa0c9c1c602f5852 Mon Sep 17 00:00:00 2001 From: CircleCI Date: Tue, 13 Oct 2026 18:44:09 +0000 Subject: [PATCH 06/27] Bump version to 0.1.0-RC.5 [skip ci] --- pom.xml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/pom.xml b/pom.xml index c7e2c3b..bbe9414 100644 --- a/pom.xml +++ b/pom.xml @@ -20,7 +20,7 @@ - 0.1.0-RC.4 + 0.1.0-RC.5 21 21 UTF-8 -- 2.54.0 From 47cc572c3687a955dbfae6124d4cb35e47c233c8 Mon Sep 17 00:00:00 2001 From: CircleCI Date: Tue, 13 Oct 2026 19:51:20 +0000 Subject: [PATCH 07/27] Bump version to 0.1.0-RC.6 [skip ci] --- pom.xml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/pom.xml b/pom.xml index bbe9414..9c3f227 100644 --- a/pom.xml +++ b/pom.xml @@ -20,7 +20,7 @@ - 0.1.0-RC.5 + 0.1.0-RC.6 21 21 UTF-8 -- 2.54.0 From 5c76a1324590944c053808662a5f2b0db855f464 Mon Sep 17 00:00:00 2001 From: CircleCI Date: Tue, 13 Oct 2026 23:31:30 +0000 Subject: [PATCH 08/27] Bump version to 0.1.0-RC.7 [skip ci] --- pom.xml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/pom.xml b/pom.xml index 519c365..9fc91e0 100644 --- a/pom.xml +++ b/pom.xml @@ -20,7 +20,7 @@ - 0.1.0-RC.6 + 0.1.0-RC.7 21 21 UTF-8 -- 2.54.0 From d4a1f64480ae5feb6f5affddc5a202e678760c0d Mon Sep 17 00:00:00 2001 From: CircleCI Date: Wed, 14 Oct 2026 00:55:57 +0000 Subject: [PATCH 09/27] Bump version to 0.1.0-RC.8 [skip ci] --- pom.xml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/pom.xml b/pom.xml index db4905a..2dc0d22 100644 --- a/pom.xml +++ b/pom.xml @@ -20,7 +20,7 @@ - 0.1.0-RC.7 + 0.1.0-RC.8 21 21 UTF-8 -- 2.54.0 From 2d8459bb24eb9c4a1a8ec5b9105f23ba138664ad Mon Sep 17 00:00:00 2001 From: CircleCI Date: Wed, 14 Oct 2026 01:42:57 +0000 Subject: [PATCH 10/27] Bump version to 0.1.0-RC.9 [skip ci] --- pom.xml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/pom.xml b/pom.xml index 98665d5..0ad7a17 100644 --- a/pom.xml +++ b/pom.xml @@ -20,7 +20,7 @@ - 0.1.0-RC.8 + 0.1.0-RC.9 21 21 UTF-8 -- 2.54.0 From e23206f1e8ed65e6d271076dc4be77c9c50b7802 Mon Sep 17 00:00:00 2001 From: CircleCI Date: Wed, 14 Oct 2026 12:12:23 +0000 Subject: [PATCH 11/27] Bump version to 0.1.0-RC.10 [skip ci] --- pom.xml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/pom.xml b/pom.xml index 0ad7a17..fbab970 100644 --- a/pom.xml +++ b/pom.xml @@ -20,7 +20,7 @@ - 0.1.0-RC.9 + 0.1.0-RC.10 21 21 UTF-8 -- 2.54.0 From cfcc044055d8aaf34938044cddd4ec7ad05d5590 Mon Sep 17 00:00:00 2001 From: CircleCI Date: Wed, 14 Oct 2026 12:57:45 +0000 Subject: [PATCH 12/27] Bump version to 0.1.0-RC.11 [skip ci] --- pom.xml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/pom.xml b/pom.xml index 7eb6816..9eca8d2 100644 --- a/pom.xml +++ b/pom.xml @@ -20,7 +20,7 @@ - 0.1.0-RC.10 + 0.1.0-RC.11 21 21 UTF-8 -- 2.54.0 From 3a7ff1c659ecf05c68318b7f65d1c000e586205e Mon Sep 17 00:00:00 2001 From: CircleCI Date: Wed, 14 Oct 2026 16:03:00 +0000 Subject: [PATCH 13/27] Bump version to 0.1.0-RC.12 [skip ci] --- pom.xml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/pom.xml b/pom.xml index 383a436..c486d5f 100644 --- a/pom.xml +++ b/pom.xml @@ -20,7 +20,7 @@ - 0.1.0-RC.11 + 0.1.0-RC.12 21 21 UTF-8 -- 2.54.0 From c1cceaf4c3a5e1451f63329589c568c674ea2799 Mon Sep 17 00:00:00 2001 From: CircleCI Date: Wed, 14 Oct 2026 22:27:30 +0000 Subject: [PATCH 14/27] Bump version to 0.1.0-RC.13 [skip ci] --- pom.xml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/pom.xml b/pom.xml index c486d5f..4016ceb 100644 --- a/pom.xml +++ b/pom.xml @@ -20,7 +20,7 @@ - 0.1.0-RC.12 + 0.1.0-RC.13 21 21 UTF-8 -- 2.54.0 From 8c33db9fcfa7aead2ed341403d98ba5e4317a7b1 Mon Sep 17 00:00:00 2001 From: CircleCI Date: Wed, 14 Oct 2026 23:13:46 +0000 Subject: [PATCH 15/27] Bump version to 0.1.0-RC.14 [skip ci] --- pom.xml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/pom.xml b/pom.xml index 4016ceb..e80aa04 100644 --- a/pom.xml +++ b/pom.xml @@ -20,7 +20,7 @@ - 0.1.0-RC.13 + 0.1.0-RC.14 21 21 UTF-8 -- 2.54.0 From 47115fe359310a32b2b01e9476d858eafa49e255 Mon Sep 17 00:00:00 2001 From: CircleCI Date: Thu, 15 Oct 2026 00:18:50 +0000 Subject: [PATCH 16/27] Bump version to 0.1.0-RC.15 [skip ci] --- pom.xml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/pom.xml b/pom.xml index 2948e79..d4cb302 100644 --- a/pom.xml +++ b/pom.xml @@ -20,7 +20,7 @@ - 0.1.0-RC.14 + 0.1.0-RC.15 21 21 UTF-8 -- 2.54.0 From dfb05d8935d6d4aee27cf536e0f7342a132df133 Mon Sep 17 00:00:00 2001 From: CircleCI Date: Thu, 15 Oct 2026 02:20:24 +0000 Subject: [PATCH 17/27] Bump version to 0.1.0-RC.16 [skip ci] --- pom.xml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/pom.xml b/pom.xml index 1beae07..b309027 100644 --- a/pom.xml +++ b/pom.xml @@ -20,7 +20,7 @@ - 0.1.0-RC.15 + 0.1.0-RC.16 21 21 UTF-8 -- 2.54.0 From d43402e262e2f12ad7c66b2540866df757a1d818 Mon Sep 17 00:00:00 2001 From: CircleCI Date: Thu, 15 Oct 2026 02:40:56 +0000 Subject: [PATCH 18/27] Bump version to 0.1.0-RC.17 [skip ci] --- pom.xml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/pom.xml b/pom.xml index fc6f25c..5df0ade 100644 --- a/pom.xml +++ b/pom.xml @@ -20,7 +20,7 @@ - 0.1.0-RC.16 + 0.1.0-RC.17 21 21 UTF-8 -- 2.54.0 From 6adf390bf8734a93e29805420a1400b91a593d48 Mon Sep 17 00:00:00 2001 From: CircleCI Date: Thu, 15 Oct 2026 03:36:35 +0000 Subject: [PATCH 19/27] Bump version to 0.1.0-RC.18 [skip ci] --- pom.xml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/pom.xml b/pom.xml index 5df0ade..5a4eff8 100644 --- a/pom.xml +++ b/pom.xml @@ -20,7 +20,7 @@ - 0.1.0-RC.17 + 0.1.0-RC.18 21 21 UTF-8 -- 2.54.0 From 2cea8ffd44283d315e4fc3821146691f8f510133 Mon Sep 17 00:00:00 2001 From: CircleCI Date: Thu, 15 Oct 2026 21:12:25 +0000 Subject: [PATCH 20/27] Bump version to 0.1.0-RC.19 [skip ci] --- pom.xml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/pom.xml b/pom.xml index 5a4eff8..b391e37 100644 --- a/pom.xml +++ b/pom.xml @@ -20,7 +20,7 @@ - 0.1.0-RC.18 + 0.1.0-RC.19 21 21 UTF-8 -- 2.54.0 From 1c33e97664a5b2807d40527dce28e287e3b7017f Mon Sep 17 00:00:00 2001 From: CircleCI Date: Thu, 15 Oct 2026 21:41:12 +0000 Subject: [PATCH 21/27] Bump version to 0.1.0-RC.20 [skip ci] --- pom.xml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/pom.xml b/pom.xml index 88d4518..4b907a0 100644 --- a/pom.xml +++ b/pom.xml @@ -20,7 +20,7 @@ - 0.1.0-RC.19 + 0.1.0-RC.20 21 21 UTF-8 -- 2.54.0 From 9d31de46a608f0985ea2ef79530ed9cc3c94b898 Mon Sep 17 00:00:00 2001 From: CircleCI Date: Fri, 16 Oct 2026 01:13:38 +0000 Subject: [PATCH 22/27] Bump version to 0.1.0-RC.21 [skip ci] --- pom.xml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/pom.xml b/pom.xml index 4b907a0..28fe32d 100644 --- a/pom.xml +++ b/pom.xml @@ -20,7 +20,7 @@ - 0.1.0-RC.20 + 0.1.0-RC.21 21 21 UTF-8 -- 2.54.0 From 82febe0f198c5ae7b9f84e8ae3d4cf3a5099f14f Mon Sep 17 00:00:00 2001 From: CircleCI Date: Fri, 16 Oct 2026 02:27:26 +0000 Subject: [PATCH 23/27] Bump version to 0.1.0-RC.22 [skip ci] --- pom.xml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/pom.xml b/pom.xml index 28fe32d..501b24b 100644 --- a/pom.xml +++ b/pom.xml @@ -20,7 +20,7 @@ - 0.1.0-RC.21 + 0.1.0-RC.22 21 21 UTF-8 -- 2.54.0 From f46735b1c055793fb64bc657ac6198f5ca08794f Mon Sep 17 00:00:00 2001 From: CircleCI Date: Fri, 16 Oct 2026 16:04:25 +0000 Subject: [PATCH 24/27] Bump version to 0.1.0-RC.23 [skip ci] --- pom.xml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/pom.xml b/pom.xml index 501b24b..7de8ed7 100644 --- a/pom.xml +++ b/pom.xml @@ -20,7 +20,7 @@ - 0.1.0-RC.22 + 0.1.0-RC.23 21 21 UTF-8 -- 2.54.0 From 2caa634044a2883b0e9122e47b0d6ca61faed638 Mon Sep 17 00:00:00 2001 From: CircleCI Date: Fri, 16 Oct 2026 17:08:39 +0000 Subject: [PATCH 25/27] Bump version to 0.1.0-RC.24 [skip ci] --- pom.xml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/pom.xml b/pom.xml index 7de8ed7..3924274 100644 --- a/pom.xml +++ b/pom.xml @@ -20,7 +20,7 @@ - 0.1.0-RC.23 + 0.1.0-RC.24 21 21 UTF-8 -- 2.54.0 From 3de664e0fd0434e740886ed9ff59ad39cf52771f Mon Sep 17 00:00:00 2001 From: James Maes Date: Wed, 21 Oct 2026 12:38:46 -0500 Subject: [PATCH 26/27] ci: centralize release branch verification and workflows (#75) Co-authored-by: Codex --- .circleci/config.yml | 68 +++++----------------------- .github/workflows/codeql.yml | 23 ++++++++++ .github/workflows/release-rc.yml | 76 ++++++++++++++++++++++++++------ .qqq-ci.yml | 15 +++++++ 4 files changed, 111 insertions(+), 71 deletions(-) create mode 100644 .github/workflows/codeql.yml create mode 100644 .qqq-ci.yml diff --git a/.circleci/config.yml b/.circleci/config.yml index aec59b2..23c1a84 100644 --- a/.circleci/config.yml +++ b/.circleci/config.yml @@ -1,60 +1,14 @@ version: 2.1 - +setup: true orbs: - qqq-orb: kingsrook/qqq-orb@0.6.9 - -commands: - configure-package-install: - steps: - - run: - name: Keep package installation unattended - command: | - # These build machines are discarded after the job. - sudo install -d /etc/needrestart/conf.d - printf '%s\n' '$nrconf{restart} = "l";' | sudo tee /etc/needrestart/conf.d/99-qqq-ci.conf >/dev/null - + qqq-orb: kingsrook/qqq-orb@0.7.15 workflows: - test_only: - jobs: - - qqq-orb/mvn_test_only: - pre-steps: - - configure-package-install - context: [qqq-maven-registry-credentials] - filters: - branches: - ignore: /(develop|release\/.*)/ - - publish_snapshot: - jobs: - - qqq-orb/mvn_publish: - pre-steps: - - configure-package-install - context: [qqq-maven-registry-credentials] - branch_type: snapshot - filters: - branches: - only: [develop] - - publish_release_candidate: - jobs: - - qqq-orb/mvn_publish: - pre-steps: - - configure-package-install - context: [qqq-maven-registry-credentials] - branch_type: release_candidate - filters: - branches: - only: [/release\/.*/] - - publish_release: - jobs: - - qqq-orb/mvn_publish: - pre-steps: - - configure-package-install - context: [qqq-maven-registry-credentials] - branch_type: release - filters: - branches: - ignore: /.*/ - tags: - only: [/^v\d+\.\d+\.\d+$/] + setup: + jobs: + - qqq-orb/generate_pipeline: + orb_version: 0.7.15 + filters: + branches: + ignore: automation/qbit-rc-pending + tags: + only: /^v\d+\.\d+\.\d+$/ diff --git a/.github/workflows/codeql.yml b/.github/workflows/codeql.yml new file mode 100644 index 0000000..bd3ceaf --- /dev/null +++ b/.github/workflows/codeql.yml @@ -0,0 +1,23 @@ +name: CodeQL +'on': + push: + branches: + - main + - develop + - release/** + - hotfix/** + pull_request: {} + schedule: + - cron: 41 16 * * 0 + workflow_dispatch: {} +permissions: + contents: read + actions: read + security-events: write + packages: read +concurrency: + group: codeql-${{ github.event.pull_request.number || github.ref }} + cancel-in-progress: ${{ github.event_name == 'pull_request' }} +jobs: + analyze: + uses: QRun-IO/qqq-orb/.github/workflows/qbit-codeql.yml@c288a8864fe9054f68410e1d78f5863aea4c4d22 diff --git a/.github/workflows/release-rc.yml b/.github/workflows/release-rc.yml index a342ba8..b48a32a 100644 --- a/.github/workflows/release-rc.yml +++ b/.github/workflows/release-rc.yml @@ -1,26 +1,74 @@ -# Cut the next release candidate (QRun-IO/qqq docs/QRUN-QBIT-REPO-STANDARD.md, C6): -# run this workflow by hand, or merge a develop PR labelled `release:rc`. It merges develop into -# the release line and the release/* push runs CircleCI publish_release_candidate. name: Release candidate - -on: +'on': workflow_dispatch: inputs: dry_run: - description: Print the plan without merging or creating branches + description: Plan a cut without changing release state type: boolean default: false + finalize: + description: Retry finalization of an existing cut; do not cut another RC + type: boolean + default: false + recover: + description: Recover the same published RC after a deployment timeout; never + reupload + type: boolean + default: false + circle_build: + description: Original Circle publisher build number (recovery only) + type: string + default: '' + merge_sha: + description: Existing cut merge_sha (finalize retry only) + type: string + default: '' + bump_sha: + description: Existing cut bump_sha (finalize retry only) + type: string + default: '' + branch: + description: Existing cut branch (finalize retry only) + type: string + default: '' + version: + description: Existing cut version (finalize retry only) + type: string + default: '' pull_request: - types: [closed] - branches: [develop] - + types: + - closed + branches: + - develop + repository_dispatch: + types: + - qbit-rc-published permissions: contents: write - + actions: read + checks: read jobs: release-rc: - if: github.event_name == 'workflow_dispatch' || (github.event.pull_request.merged == true && contains(github.event.pull_request.labels.*.name, 'release:rc')) - uses: QRun-IO/.github/.github/workflows/qbit-release-rc.yml@main - secrets: inherit + if: github.event_name == 'workflow_dispatch' || github.event_name == 'repository_dispatch' + || (github.event_name == 'pull_request' && github.event.pull_request.merged + == true && github.event.pull_request.base.ref == 'develop' && contains(github.event.pull_request.labels.*.name, + 'release:rc')) + uses: QRun-IO/qqq-orb/.github/workflows/qbit-release-rc.yml@cc30f599e63c8e41629c99d23395bac1adb8bc44 with: - dry_run: ${{ github.event_name == 'workflow_dispatch' && inputs.dry_run }} + dry_run: ${{ github.event_name == 'workflow_dispatch' && inputs.dry_run || false + }} + finalize: ${{ github.event_name == 'repository_dispatch' || (github.event_name + == 'workflow_dispatch' && inputs.finalize) }} + recover: ${{ github.event_name == 'workflow_dispatch' && inputs.recover || false + }} + circle_build: ${{ github.event_name == 'workflow_dispatch' && inputs.circle_build + || '' }} + merge_sha: ${{ github.event_name == 'repository_dispatch' && github.event.client_payload.merge_sha + || inputs.merge_sha || '' }} + bump_sha: ${{ github.event_name == 'repository_dispatch' && github.event.client_payload.bump_sha + || inputs.bump_sha || '' }} + branch: ${{ github.event_name == 'repository_dispatch' && github.event.client_payload.branch + || inputs.branch || '' }} + version: ${{ github.event_name == 'repository_dispatch' && github.event.client_payload.version + || inputs.version || '' }} + secrets: inherit diff --git a/.qqq-ci.yml b/.qqq-ci.yml new file mode 100644 index 0000000..58923b8 --- /dev/null +++ b/.qqq-ci.yml @@ -0,0 +1,15 @@ +profile: qbit +orb_version: 0.7.15 +default_branch: develop +java_version: '21' +browsers: true +api_checks: false +docs_only: false +release_on_main: false +release_tag_policy: stable +test_branch_policy: non-release +publish: + snapshot: true + release_candidate: true + release: true + asciidoc: false -- 2.54.0 From c5125f9788656b68a2d8b54ae081ba2894d409ea Mon Sep 17 00:00:00 2001 From: James Maes Date: Wed, 4 Nov 2026 19:15:44 -0600 Subject: [PATCH 27/27] fix(assistant): store the transcript before message.end so the panel shows the answer The panel reloads the conversation the moment the stream ends. The redacted transcript (message parts, pending actions, stored events) was flushed only after message.end was sent, so that reload read the "[]" placeholders and showed the newest question and answer as empty bubbles until the panel was closed and reopened. Persist and flush first, then emit message.end. Co-Authored-By: Claude Sonnet 5.5 --- CHANGELOG.md | 4 ++ .../qbits/assistant/engine/TurnEngine.java | 11 ++++++ .../assistant/engine/TurnEngineTest.java | 21 +++++++++++ .../qbits/assistant/testing/TurnRunner.java | 37 +++++++++++++++++++ 4 files changed, 73 insertions(+) diff --git a/CHANGELOG.md b/CHANGELOG.md index 688af47..45aa3ef 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,5 +1,9 @@ # Changelog +## Unreleased - Answer visible at the end of the stream + +- The turn's redacted transcript (the question and answer parts, pending actions, stored events) is now written before `message.end` is sent. The panel reloads the conversation the moment the stream ends, and used to read the empty placeholders (`[]`) the transcript redactor stores until its flush, so the newest question and answer showed as empty bubbles until the panel was closed and reopened. + ## Unreleased - Optional provider health - An enabled provider that is not the default and not the only route for any configured routing (an enabled profile's simple/complex model, a budget downgrade model or a knowledge source's embedding model, with no fallback on another usable provider) is optional: when it is unreachable, its `assistant.provider.` and `assistant.model.` checks report DEGRADED with a message naming the provider requests use instead, so one unfinished local server no longer turns the whole `assistant` component DOWN. The default provider and sole routes still report DOWN. A failed provider probe still carries `reachable=false`, so the circuit breaker keeps refusing the provider. diff --git a/qbit-assistant/src/main/java/com/kingsrook/qbits/assistant/engine/TurnEngine.java b/qbit-assistant/src/main/java/com/kingsrook/qbits/assistant/engine/TurnEngine.java index 38a264e..df50808 100644 --- a/qbit-assistant/src/main/java/com/kingsrook/qbits/assistant/engine/TurnEngine.java +++ b/qbit-assistant/src/main/java/com/kingsrook/qbits/assistant/engine/TurnEngine.java @@ -1321,6 +1321,17 @@ public class TurnEngine implements Runnable String finalErrorText = errorText; safely(() -> emit(TurnEvent.ERROR, map("code", finalErrorCode, "message", finalErrorText))); } + + /////////////////////////////////////////////////////////////////////// + // the stream ends at message.end and the panel reloads the // + // conversation at once, so the redacted transcript (message parts, // + // pending actions, events) must be stored before that event is sent // + /////////////////////////////////////////////////////////////////////// + if(!alreadyTerminal) + { + safely(this::persistPendingEvents); + safely(() -> transcript.flush(store)); + } safely(() -> emit(TurnEvent.MESSAGE_END, map("messageId", assistantMessage.getId(), "status", finalStatus, "stopReason", finalStopReason))); if(!alreadyTerminal) { diff --git a/qbit-assistant/src/test/java/com/kingsrook/qbits/assistant/engine/TurnEngineTest.java b/qbit-assistant/src/test/java/com/kingsrook/qbits/assistant/engine/TurnEngineTest.java index 8bab8ea..7660df2 100644 --- a/qbit-assistant/src/test/java/com/kingsrook/qbits/assistant/engine/TurnEngineTest.java +++ b/qbit-assistant/src/test/java/com/kingsrook/qbits/assistant/engine/TurnEngineTest.java @@ -141,6 +141,27 @@ class TurnEngineTest extends BaseAssistantTest + /******************************************************************************* + ** The panel reloads the conversation as soon as message.end arrives, so the + ** question and the answer must already be stored then, not after it. + *******************************************************************************/ + @Test + void testMessagesAreStoredBeforeMessageEnd() throws Exception + { + provider.thenText("Four posts are published."); + + TurnRunner.Outcome outcome = runner.run(EDITOR, "how many published posts?"); + assertThat(outcome.end()).containsEntry("status", "COMPLETE"); + + List stored = runner.messagesAtEnd(); + assertThat(stored).hasSize(2); + assertThat(stored).extracting(AssistantMessage::getPartsJson).allSatisfy(parts -> assertThat(parts).isNotEqualTo("[]")); + assertThat(stored.get(0).getPartsJson()).contains("how many published posts?"); + assertThat(stored.get(1).getPartsJson()).contains("Four posts are published."); + } + + + /******************************************************************************* ** Parallel tool calls: all results in ONE user message, in order. *******************************************************************************/ diff --git a/qbit-assistant/src/test/java/com/kingsrook/qbits/assistant/testing/TurnRunner.java b/qbit-assistant/src/test/java/com/kingsrook/qbits/assistant/testing/TurnRunner.java index 82a3df2..00f17ac 100644 --- a/qbit-assistant/src/test/java/com/kingsrook/qbits/assistant/testing/TurnRunner.java +++ b/qbit-assistant/src/test/java/com/kingsrook/qbits/assistant/testing/TurnRunner.java @@ -27,6 +27,7 @@ import com.kingsrook.qbits.assistant.AssistantRuntime; import com.kingsrook.qbits.assistant.api.AssistantApi; import com.kingsrook.qbits.assistant.engine.RunningTurn; import com.kingsrook.qbits.assistant.engine.TurnEvent; +import com.kingsrook.qbits.assistant.model.AssistantMessage; import com.kingsrook.qbits.assistant.model.AssistantToolCall; import com.kingsrook.qbits.assistant.persistence.AssistantStore; import com.kingsrook.qqq.backend.core.context.QContext; @@ -47,6 +48,8 @@ public class TurnRunner private final QInstance qInstance; private final AssistantQBitConfig config; + private List messagesAtEnd = List.of(); + /******************************************************************************* @@ -169,6 +172,10 @@ public class TurnRunner { List batch = running.getLog().after(last, 1000); events.addAll(batch); + if(batch.stream().anyMatch(e -> TurnEvent.MESSAGE_END.equals(e.getType()))) + { + messagesAtEnd = messagesOf(userId, convId); + } if(!batch.isEmpty()) { last = batch.get(batch.size() - 1).getSeq(); @@ -194,6 +201,36 @@ public class TurnRunner + /******************************************************************************* + ** The conversation's stored messages, read the moment the run's message.end + ** was seen: what the panel finds when it reloads the conversation at the end + ** of the stream. + *******************************************************************************/ + public List messagesAtEnd() + { + return (messagesAtEnd); + } + + + + /******************************************************************************* + ** The stored messages of a conversation (read as the user). + *******************************************************************************/ + private List messagesOf(String userId, Integer conversationId) throws Exception + { + QContext.init(qInstance, AssistantTestFixture.ownerSession(userId, config)); + try + { + return (new AssistantStore().listMessages(userId, conversationId, null, 50)); + } + finally + { + QContext.clear(); + } + } + + + /******************************************************************************* ** The audited tool calls of a turn (read as the user). *******************************************************************************/ -- 2.54.0