Re-pins qbit-webhooks to com.kingsrook:qbit-build-parent:2.0.0 (qqq 4.0.0 GA, Java 21) and verifies it on qqq 4.1.0-SNAPSHOT, so qqq-all can include it. Before this change develop could not build: it imported qqq-bom-pom:0.40.0-SNAPSHOT, which no longer exists on the Central snapshots repo (its maven-metadata.xml returns 404).
Merge this PR with a merge commit. Do not squash or rebase. The first commit is a git merge --no-ff origin/main back-merge. Squashing or rebasing drops main from develop's history, and the next develop-to-main release would hit the same conflicts again.
Changes
9c063bfchore: merge main into develop. Main had 8 commits that develop lacked. This brings in main's Java 21 parent (1.5.1), the 0.3.0 release, the Apache-2.0 LICENSE and NOTICE, orb 0.5.0, .github/release.yml and the standardized README. Develop keeps its CODEOWNERS and CLAUDE.md. The conflict resolutions are listed below.
d247432build: re-pin to qbit-build-parent 2.0.0 (qqq 4.0.0). Parent 1.5.1 goes to 2.0.0. The child's qqq-bom-pom import shadowed the parent's BOM, so it is removed. There were no explicit qqq module <version>s to remove. Three qqq 4.0 API removals broke the build and are migrated:
BREAK-04-06, main source: WebhookEventTypeCustomizerInterface.buildEventContent called the removed JsonUtils.toJson(Object, Consumer<ObjectMapper>). It now calls toJsonCustomized(postBody, builder -> builder.serializationInclusion(JsonInclude.Include.ALWAYS)), the replacement the 4.0 migration guide gives for builder-level settings. Null fields still appear in webhook post bodies.
BREAK-01, test only: com.kingsrook.qqq.backend.javalin.QJavalinMetaData is now com.kingsrook.qqq.middleware.javalin.QJavalinMetaData.
BREAK-04-11, test only: setAuthentication(...) is now withInstanceDefaultAuthentication(...) (same as the B2 re-pin).
The README prerequisite now reads "QQQ application (v4.0.0+)". It said v0.35.0+.
c8b9853test: assert webhook post body keeps null-valued fields. New WebhookEventTypeCustomizerInterfaceTest. No existing test covered the ALWAYS inclusion that the removed overload provided. Without it, the obvious fix (JsonUtils.toJson(postBody)) would silently drop null fields from every webhook payload.
dfeb3fabuild: add opt-in qqq-snapshot profile.-Pqqq-snapshot imports qqq-bom-pom:${qqq.snapshot.version} (default 4.1.0-SNAPSHOT) ahead of the parent's BOM and adds the Central snapshots repository. Default builds still get qqq only from the parent. This is the same profile as the B1 quick-search re-pin.
b1df0d6ci: keep package installs unattended on CircleCI. Copies the configure-package-install command from qbit-quick-search develop and adds it as a pre-steps entry on all 5 jobs. This stops needrestart's interactive prompt from hanging install_java (QRun-IO/qqq-orb#11). The orb version stays at main's 0.5.0.
bfb0706docs: refresh repo agent notes for the parent 2.0.0 re-pin. In CLAUDE.md, three of the four cautions (branch divergence, the frozen SNAPSHOT, the test-code breaks) no longer apply after this PR. They are replaced with the qqq-snapshot usage and the licensing and README caveats below.
Merge conflict resolutions (9c063bf)
File
develop
main
Resolution
LICENSE
AGPL-3.0 text with a QRun-IO preamble (0cbbff6, 2025-12-25)
Apache-2.0 (0e61d75, 2026-01-05)
Took main. It is the later change, it is what 0.3.0 was released under, and it matches main's NOTICE, which merged in cleanly. The B2 back-merge resolved it the same way. No license text was edited. Aligning the pom and headers is still an owner decision (see notes).
.circleci/config.yml
kingsrook/qqq-orb@0.3.8
kingsrook/qqq-orb@0.5.0
Took main. 0.5.0 is the newer orb and the one paired with the Java 21 build.
pom.xml parent
qbit-build-parent 1.4.0 (Java 17)
1.5.1 (Java 21)
Took main, the fix. The next commit moves it to 2.0.0.
pom.xml<properties>
Moved <properties> to the top, revision 0.2.1
Kept <properties> below <scm>, revision 0.3.0
Kept develop's layout with main's value, 0.3.0, so there is a single <properties> block. 0.3.0 is already released. On develop the orb turns a stable version into the next minor SNAPSHOT, so this does not republish 0.3.0. I did not pick a next release number.
README.md
Old README plus a CircleCI badge and wiki 404 link fixes (f8a8e6a)
Standardized rewrite (69fc7d0, f346c24)
Took main. The rewrite removes the sections develop's link fixes touched, so nothing develop fixed is lost. Caveat: main's README documents an API this code does not have (see notes).
The qqq-bom-pom 0.40.0-SNAPSHOT line merged without a conflict, because main never changed it. The next commit removes it.
Parent set to 2.0.0 and BOM import removed: mvn -B clean verify
FAIL: compile error, method toJson in class JsonUtils cannot be applied to given types (BREAK-04-06)
4
New test added, with JsonUtils.toJson(postBody) as the migration
FAIL: testBuildEventContentKeepsNullValues: null-valued field should be present in the post body (32 tests, 1 failure)
5
Migrated with toJsonCustomized(... ALWAYS): mvn -B clean verify
BUILD SUCCESS, 32 tests, 0 failures
6
Before the profile: mvn -B dependency:tree -Pqqq-snapshot -Dincludes=com.kingsrook.qqq:qqq-backend-core
profile "qqq-snapshot" ... does not exist, resolves 4.0.0 (red)
7
After the profile, same command
qqq-backend-core, qqq-middleware-api, qqq-middleware-javalin and qqq-openapi all resolve 4.1.0-SNAPSHOT. Without -P they resolve 4.0.0. With -Dqqq.snapshot.version=4.0.0 they resolve 4.0.0
8
mvn -B help:effective-pom, then javap -v on a compiled class
Compiler source/target 21, class file major version 65
9
yq parse of .circleci/config.yml, compared with qbit-quick-search develop
commands block identical. All 5 jobs have pre-steps: [configure-package-install]
Final gates (on a63a53e, whose tree is identical to HEAD bfb0706; the amend only reworded the commit message):
BUILD SUCCESS, 32 tests, 0 failures. Resolved qqq-backend-core-4.1.0-20260926.013614-9.jar from sonatype-snapshots
4.0.0, published artifacts only
mvn -B clean verify -Dmaven.repo.local=/tmp/m2-b5
BUILD SUCCESS, 32 tests, 0 failures
I added the isolated-repo gates because the shared ~/.m2 can hold locally installed snapshots. mvn install was not run.
Unit tests pass (mvn test)
Integration tests pass (mvn verify)
Manual testing performed
Checklist
My code follows the project's style guidelines
I have performed a self-review of my own code
I have commented my code, particularly in hard-to-understand areas (the new test and the profile are commented)
I have made corresponding changes to the documentation
My changes generate no new warnings (see the parent warning below)
I have added tests that prove my fix is effective or that my feature works
New and existing unit tests pass locally with my changes
Additional Notes
Docker-backed tests: none. Every suite uses the in-memory backend, and the sender tests use a local Javalin receiver. The "fail, not skip, when CI=true" rule has nothing to change here.
CircleCI:qqq-orb/mvn_test_only build 35 passed on this branch (workflow test_only, 1m23s). It got past the install_java step that hung on the needrestart prompt in B2's build 44. I could not run circleci config validate locally, because the CLI has no API token to resolve orbs.
Upstream parent warning, not fixed here: each build logs [WARNING] Failed to build parent project for com.kingsrook.qbits:qbit-webhooks:jar:0.3.0. The published qbit-build-parent-2.0.0.pom declares its <version> as a literal ${revision} because it was not flattened. The effective model is still correct (qqq 4.0.0, Java 21). The fix belongs in qbit-bom.
Left out of scope (owner decisions or follow-ups):
Licensing metadata still disagrees with itself. LICENSE and NOTICE say Apache-2.0, while the pom <licenses>, all 59 Java headers and checkstyle/license.txt say AGPL-3.0, and the README footer says "Proprietary". None of these were edited.
Main's README documents APIs that do not exist in this code: new WebhooksQBit().configure(...), QWebhookMetaData, HMAC X-Webhook-Signature signing, and a webhook_log table. The real entry points are WebhooksQBitProducer, WebhooksRegistry and WebhookEventType, which the old develop README described. The README needs a rewrite against the source.
The Dependabot alert on the default branch (assertj-core XXE, test scope) and its open bump PR.
Kingsrook org URLs in <url> and <scm>.
Not merged. This needs review first.
## Summary
Re-pins `qbit-webhooks` to `com.kingsrook:qbit-build-parent:2.0.0` (qqq 4.0.0 GA, Java 21) and verifies it on qqq `4.1.0-SNAPSHOT`, so qqq-all can include it. Before this change develop could not build: it imported `qqq-bom-pom:0.40.0-SNAPSHOT`, which no longer exists on the Central snapshots repo (its `maven-metadata.xml` returns 404).
**Merge this PR with a merge commit. Do not squash or rebase.** The first commit is a `git merge --no-ff origin/main` back-merge. Squashing or rebasing drops main from develop's history, and the next develop-to-main release would hit the same conflicts again.
## Changes
- `9c063bf` **chore: merge main into develop.** Main had 8 commits that develop lacked. This brings in main's Java 21 parent (1.5.1), the 0.3.0 release, the Apache-2.0 `LICENSE` and `NOTICE`, orb 0.5.0, `.github/release.yml` and the standardized README. Develop keeps its `CODEOWNERS` and `CLAUDE.md`. The conflict resolutions are listed below.
- `d247432` **build: re-pin to qbit-build-parent 2.0.0 (qqq 4.0.0).** Parent 1.5.1 goes to 2.0.0. The child's `qqq-bom-pom` import shadowed the parent's BOM, so it is removed. There were no explicit qqq module `<version>`s to remove. Three qqq 4.0 API removals broke the build and are migrated:
- BREAK-04-06, main source: `WebhookEventTypeCustomizerInterface.buildEventContent` called the removed `JsonUtils.toJson(Object, Consumer<ObjectMapper>)`. It now calls `toJsonCustomized(postBody, builder -> builder.serializationInclusion(JsonInclude.Include.ALWAYS))`, the replacement the 4.0 migration guide gives for builder-level settings. Null fields still appear in webhook post bodies.
- BREAK-01, test only: `com.kingsrook.qqq.backend.javalin.QJavalinMetaData` is now `com.kingsrook.qqq.middleware.javalin.QJavalinMetaData`.
- BREAK-04-11, test only: `setAuthentication(...)` is now `withInstanceDefaultAuthentication(...)` (same as the B2 re-pin).
- The README prerequisite now reads "QQQ application (v4.0.0+)". It said v0.35.0+.
- `c8b9853` **test: assert webhook post body keeps null-valued fields.** New `WebhookEventTypeCustomizerInterfaceTest`. No existing test covered the `ALWAYS` inclusion that the removed overload provided. Without it, the obvious fix (`JsonUtils.toJson(postBody)`) would silently drop null fields from every webhook payload.
- `dfeb3fa` **build: add opt-in qqq-snapshot profile.** `-Pqqq-snapshot` imports `qqq-bom-pom:${qqq.snapshot.version}` (default `4.1.0-SNAPSHOT`) ahead of the parent's BOM and adds the Central snapshots repository. Default builds still get qqq only from the parent. This is the same profile as the B1 quick-search re-pin.
- `b1df0d6` **ci: keep package installs unattended on CircleCI.** Copies the `configure-package-install` command from qbit-quick-search develop and adds it as a `pre-steps` entry on all 5 jobs. This stops needrestart's interactive prompt from hanging `install_java` (QRun-IO/qqq-orb#11). The orb version stays at main's 0.5.0.
- `bfb0706` **docs: refresh repo agent notes for the parent 2.0.0 re-pin.** In `CLAUDE.md`, three of the four cautions (branch divergence, the frozen SNAPSHOT, the test-code breaks) no longer apply after this PR. They are replaced with the `qqq-snapshot` usage and the licensing and README caveats below.
### Merge conflict resolutions (`9c063bf`)
| File | develop | main | Resolution |
|---|---|---|---|
| `LICENSE` | AGPL-3.0 text with a QRun-IO preamble (`0cbbff6`, 2025-12-25) | Apache-2.0 (`0e61d75`, 2026-01-05) | **Took main.** It is the later change, it is what 0.3.0 was released under, and it matches main's `NOTICE`, which merged in cleanly. The B2 back-merge resolved it the same way. No license text was edited. Aligning the pom and headers is still an owner decision (see notes). |
| `.circleci/config.yml` | `kingsrook/qqq-orb@0.3.8` | `kingsrook/qqq-orb@0.5.0` | **Took main.** 0.5.0 is the newer orb and the one paired with the Java 21 build. |
| `pom.xml` parent | `qbit-build-parent` 1.4.0 (Java 17) | 1.5.1 (Java 21) | **Took main**, the fix. The next commit moves it to 2.0.0. |
| `pom.xml` `<properties>` | Moved `<properties>` to the top, `revision` 0.2.1 | Kept `<properties>` below `<scm>`, `revision` 0.3.0 | **Kept develop's layout with main's value, 0.3.0**, so there is a single `<properties>` block. 0.3.0 is already released. On develop the orb turns a stable version into the next minor SNAPSHOT, so this does not republish 0.3.0. I did not pick a next release number. |
| `README.md` | Old README plus a CircleCI badge and wiki 404 link fixes (`f8a8e6a`) | Standardized rewrite (`69fc7d0`, `f346c24`) | **Took main.** The rewrite removes the sections develop's link fixes touched, so nothing develop fixed is lost. Caveat: main's README documents an API this code does not have (see notes). |
The `qqq-bom-pom` 0.40.0-SNAPSHOT line merged without a conflict, because main never changed it. The next commit removes it.
## Related Issues
Closes QRun-IO/qqq#767
Part of QRun-IO/qqq-all#1
## Type of Change
- [x] Bug fix (non-breaking change that fixes an issue): develop did not build (unresolvable BOM)
- [ ] New feature (non-breaking change that adds functionality)
- [x] Breaking change (fix or feature that would cause existing functionality to not work as expected): consumers now get qqq 4.0.0 transitively
- [ ] Documentation update
- [ ] Refactoring (no functional changes)
- [x] Dependencies update
## Testing
No `mvnw` in the repo, so these commands use plain `mvn` (3.9.16, JDK 21.0.12.1). Test-first: each change was run red before it was made green.
| # | Command (in this checkout) | Result |
|---|---|---|
| 1 | `curl .../maven-snapshots/com/kingsrook/qqq/qqq-bom-pom/0.40.0-SNAPSHOT/maven-metadata.xml` | HTTP 404. The same path for `4.1.0-SNAPSHOT` returns 200 |
| 2 | On the merge commit: `mvn -B -U verify -Dmaven.repo.local=/tmp/m2-b5` | **FAIL**: `Non-resolvable import POM ... qqq-bom-pom:pom:0.40.0-SNAPSHOT (absent)` |
| 3 | Parent set to 2.0.0 and BOM import removed: `mvn -B clean verify` | **FAIL**: compile error, `method toJson in class JsonUtils cannot be applied to given types` (BREAK-04-06) |
| 4 | New test added, with `JsonUtils.toJson(postBody)` as the migration | **FAIL**: `testBuildEventContentKeepsNullValues: null-valued field should be present in the post body` (32 tests, 1 failure) |
| 5 | Migrated with `toJsonCustomized(... ALWAYS)`: `mvn -B clean verify` | BUILD SUCCESS, 32 tests, 0 failures |
| 6 | Before the profile: `mvn -B dependency:tree -Pqqq-snapshot -Dincludes=com.kingsrook.qqq:qqq-backend-core` | `profile "qqq-snapshot" ... does not exist`, resolves 4.0.0 (red) |
| 7 | After the profile, same command | `qqq-backend-core`, `qqq-middleware-api`, `qqq-middleware-javalin` and `qqq-openapi` all resolve `4.1.0-SNAPSHOT`. Without `-P` they resolve `4.0.0`. With `-Dqqq.snapshot.version=4.0.0` they resolve `4.0.0` |
| 8 | `mvn -B help:effective-pom`, then `javap -v` on a compiled class | Compiler `source`/`target` 21, class file major version 65 |
| 9 | `yq` parse of `.circleci/config.yml`, compared with qbit-quick-search develop | `commands` block identical. All 5 jobs have `pre-steps: [configure-package-install]` |
Final gates (on `a63a53e`, whose tree is identical to HEAD `bfb0706`; the amend only reworded the commit message):
| Gate | Command | Result |
|---|---|---|
| qqq 4.0.0 (parent) | `mvn -B verify` (also `mvn -B clean verify`) | BUILD SUCCESS, 32 tests, 0 failures, 0 errors, 0 skipped |
| qqq 4.1.0-SNAPSHOT | `mvn -B verify -Pqqq-snapshot` (also `mvn -B -U clean verify -Pqqq-snapshot`) | BUILD SUCCESS, 32 tests, 0 failures, 0 errors, 0 skipped |
| 4.1.0-SNAPSHOT, published artifacts only | `mvn -B -U clean verify -Pqqq-snapshot -Dmaven.repo.local=/tmp/m2-b5` | BUILD SUCCESS, 32 tests, 0 failures. Resolved `qqq-backend-core-4.1.0-20260926.013614-9.jar` from `sonatype-snapshots` |
| 4.0.0, published artifacts only | `mvn -B clean verify -Dmaven.repo.local=/tmp/m2-b5` | BUILD SUCCESS, 32 tests, 0 failures |
I added the isolated-repo gates because the shared `~/.m2` can hold locally installed snapshots. `mvn install` was not run.
- [x] Unit tests pass (`mvn test`)
- [x] Integration tests pass (`mvn verify`)
- [ ] Manual testing performed
## Checklist
- [x] My code follows the project's style guidelines
- [x] I have performed a self-review of my own code
- [x] I have commented my code, particularly in hard-to-understand areas (the new test and the profile are commented)
- [x] I have made corresponding changes to the documentation
- [ ] My changes generate no new warnings (see the parent warning below)
- [x] I have added tests that prove my fix is effective or that my feature works
- [x] New and existing unit tests pass locally with my changes
## Additional Notes
- **Docker-backed tests:** none. Every suite uses the in-memory backend, and the sender tests use a local Javalin receiver. The "fail, not skip, when `CI=true`" rule has nothing to change here.
- **CircleCI:** `qqq-orb/mvn_test_only` build 35 passed on this branch (workflow `test_only`, 1m23s). It got past the `install_java` step that hung on the needrestart prompt in B2's build 44. I could not run `circleci config validate` locally, because the CLI has no API token to resolve orbs.
- **Upstream parent warning, not fixed here:** each build logs `[WARNING] Failed to build parent project for com.kingsrook.qbits:qbit-webhooks:jar:0.3.0`. The published `qbit-build-parent-2.0.0.pom` declares its `<version>` as a literal `${revision}` because it was not flattened. The effective model is still correct (qqq 4.0.0, Java 21). The fix belongs in `qbit-bom`.
- **Left out of scope (owner decisions or follow-ups):**
- Licensing metadata still disagrees with itself. `LICENSE` and `NOTICE` say Apache-2.0, while the pom `<licenses>`, all 59 Java headers and `checkstyle/license.txt` say AGPL-3.0, and the README footer says "Proprietary". None of these were edited.
- Main's README documents APIs that do not exist in this code: `new WebhooksQBit().configure(...)`, `QWebhookMetaData`, HMAC `X-Webhook-Signature` signing, and a `webhook_log` table. The real entry points are `WebhooksQBitProducer`, `WebhooksRegistry` and `WebhookEventType`, which the old develop README described. The README needs a rewrite against the source.
- The Dependabot alert on the default branch (assertj-core XXE, test scope) and its open bump PR.
- `Kingsrook` org URLs in `<url>` and `<scm>`.
- Not merged. This needs review first.
Nothing blocks merge. I reviewed HEAD bfb0706 against plan task B5 and the Global Constraints in qqq-all/docs/plan.md.
Routing: Deep tier (1329 changed lines across 11 files, mostly LICENSE and README text from main). Risk signals: CircleCI config, and the parent/BOM change in pom.xml.
Blocking
None.
Non-blocking
The back-merge's LICENSE/NOTICE resolution needs James's call. Taking main's Apache-2.0 LICENSE and adding NOTICE changes develop's LICENSE file, which on its face conflicts with "do not change LICENSE files". Keeping develop's AGPL file would flip main back to AGPL at the next release. No license text was edited. B2 resolved this the same way and B2's review left it for James, so I'm not blocking on it either.
Merge method. Step 1 asked for a separate back-merge PR, and this PR folds the back-merge in. As the description says, merge with a merge commit. A squash or rebase drops main (0e61d75) as a parent of 9c063bf.
Duplicate snapshot repository (optional).qbit-build-parent 2.0.0 already declares central-portal-snapshots with the same URL. The profile's sonatype-snapshots entry adds a second repository, so with -Pqqq-snapshot Maven checks snapshot metadata twice. The plan requires the profile to add the repository, so keep the entry. Using the id central-portal-snapshots would replace the parent's entry instead of duplicating it.
README version mismatch. The prerequisite now says qqq v4.0.0+, but the install snippet still pins 0.3.0, which main released on qqq-bom-pom 0.27.9. Update the snippet when the next version ships. This comes on top of the README describing APIs that don't exist here, which you already flagged.
Test scope (optional).ALWAYS restores empty values as well as nulls: both toJson defaults (NON_NULL, then NON_EMPTY) would drop "" and empty collections. An assertion with .withValue("lastName", "") would cover that half of the old behavior.
Carried over. The parent's unflattened ${revision} warning belongs in qbit-bom. checkstyle/config.xml isn't wired into the build (see Verified).
Verified (on bfb0706 in the implementer's checkout; no mvn install)
qqq-backend-core, qqq-middleware-api, qqq-middleware-javalin and qqq-openapi resolve 4.0.0 / 4.1.0-SNAPSHOT
Java 21
Parent java.version 21; WebhookEventTypeCustomizerInterface.class is major version 65
toJsonCustomized migration (BREAK-04-06)
Equivalent. In the qqq 4.0.0 jar, newJsonMapperBuilder() applies the same defaults the removed newObjectMapper() path applied: JavaTimeModule, NON_NULL then NON_EMPTY, FAIL_ON_EMPTY_BEANS off, WRITE_DATES_AS_TIMESTAMPS off. serializationInclusion(ALWAYS) then replaces the default inclusion. toJson(Object) now delegates to toJsonCustomized(obj, null).
New test catches the regression
In a scratch copy, I replaced the call with JsonUtils.toJson(postBody). testBuildEventContentKeepsNullValues fails with "null-valued field should be present in the post body" (32 tests, 1 failure).
Back-merge 9c063bf
Parents are origin/develop and origin/main. Against main it differs only in CODEOWNERS, CLAUDE.md and pom layout/BOM. Main has no src changes that develop lacks, so no source was lost.
Checkstyle (checkstyle/config.xml, run by hand because the build doesn't wire it in)
1 violation, a header mismatch in ClearWebhookSubscriptionHelperMemoizationsTableCustomizer.java. This PR doesn't touch that file, so it predates the PR. Changed and new files: 0 violations
CircleCI
mvn_test_only build 35 is green. mvn_publish uses the same java executor as mvn_test_only in orb 0.5.0, so the configure-package-install pre-step applies to the publish jobs the same way. The command block matches qbit-quick-search develop.
Step 4 (Docker ITs)
No Testcontainers or Docker tests
Step 5 (explicit qqq <version>s)
None. qqq-backend-core and qqq-middleware-api are versionless. The only qqq version is the profile's ${qqq.snapshot.version} BOM import.
Secrets
None. The only grep hits are LICENSE boilerplate and README placeholders (your-signing-secret) that came from main
Not merged. The merge decision is James's.
## Review, round 1 (plan task B5): approve
Nothing blocks merge. I reviewed HEAD `bfb0706` against plan task B5 and the Global Constraints in `qqq-all/docs/plan.md`.
Routing: Deep tier (1329 changed lines across 11 files, mostly LICENSE and README text from main). Risk signals: CircleCI config, and the parent/BOM change in `pom.xml`.
### Blocking
None.
### Non-blocking
1. **The back-merge's LICENSE/NOTICE resolution needs James's call.** Taking main's Apache-2.0 `LICENSE` and adding `NOTICE` changes develop's LICENSE file, which on its face conflicts with "do not change LICENSE files". Keeping develop's AGPL file would flip `main` back to AGPL at the next release. No license text was edited. B2 resolved this the same way and B2's review left it for James, so I'm not blocking on it either.
2. **Merge method.** Step 1 asked for a separate back-merge PR, and this PR folds the back-merge in. As the description says, merge with a merge commit. A squash or rebase drops `main` (`0e61d75`) as a parent of `9c063bf`.
3. **Duplicate snapshot repository (optional).** `qbit-build-parent` 2.0.0 already declares `central-portal-snapshots` with the same URL. The profile's `sonatype-snapshots` entry adds a second repository, so with `-Pqqq-snapshot` Maven checks snapshot metadata twice. The plan requires the profile to add the repository, so keep the entry. Using the id `central-portal-snapshots` would replace the parent's entry instead of duplicating it.
4. **README version mismatch.** The prerequisite now says qqq v4.0.0+, but the install snippet still pins `0.3.0`, which main released on `qqq-bom-pom` 0.27.9. Update the snippet when the next version ships. This comes on top of the README describing APIs that don't exist here, which you already flagged.
5. **Test scope (optional).** `ALWAYS` restores empty values as well as nulls: both `toJson` defaults (NON_NULL, then NON_EMPTY) would drop `""` and empty collections. An assertion with `.withValue("lastName", "")` would cover that half of the old behavior.
6. **Carried over.** The parent's unflattened `${revision}` warning belongs in `qbit-bom`. `checkstyle/config.xml` isn't wired into the build (see Verified).
### Verified (on `bfb0706` in the implementer's checkout; no `mvn install`)
| Check | Result |
|---|---|
| `mvn -B clean verify` (qqq 4.0.0 from the parent) | BUILD SUCCESS, 32 tests, 0 failures, 0 errors, 0 skipped |
| `mvn -B clean verify -Pqqq-snapshot` | BUILD SUCCESS, 32 tests, 0 failures, 0 errors, 0 skipped |
| `dependency:tree -Dincludes=com.kingsrook.qqq`, default / `-Pqqq-snapshot` | `qqq-backend-core`, `qqq-middleware-api`, `qqq-middleware-javalin` and `qqq-openapi` resolve `4.0.0` / `4.1.0-SNAPSHOT` |
| Java 21 | Parent `java.version` 21; `WebhookEventTypeCustomizerInterface.class` is major version 65 |
| `toJsonCustomized` migration (BREAK-04-06) | Equivalent. In the qqq 4.0.0 jar, `newJsonMapperBuilder()` applies the same defaults the removed `newObjectMapper()` path applied: JavaTimeModule, NON_NULL then NON_EMPTY, `FAIL_ON_EMPTY_BEANS` off, `WRITE_DATES_AS_TIMESTAMPS` off. `serializationInclusion(ALWAYS)` then replaces the default inclusion. `toJson(Object)` now delegates to `toJsonCustomized(obj, null)`. |
| New test catches the regression | In a scratch copy, I replaced the call with `JsonUtils.toJson(postBody)`. `testBuildEventContentKeepsNullValues` fails with "null-valued field should be present in the post body" (32 tests, 1 failure). |
| Back-merge `9c063bf` | Parents are `origin/develop` and `origin/main`. Against `main` it differs only in `CODEOWNERS`, `CLAUDE.md` and pom layout/BOM. Main has no `src` changes that develop lacks, so no source was lost. |
| Checkstyle (`checkstyle/config.xml`, run by hand because the build doesn't wire it in) | 1 violation, a header mismatch in `ClearWebhookSubscriptionHelperMemoizationsTableCustomizer.java`. This PR doesn't touch that file, so it predates the PR. Changed and new files: 0 violations |
| CircleCI | `mvn_test_only` build 35 is green. `mvn_publish` uses the same `java` executor as `mvn_test_only` in orb 0.5.0, so the `configure-package-install` pre-step applies to the publish jobs the same way. The command block matches qbit-quick-search develop. |
| Step 4 (Docker ITs) | No Testcontainers or Docker tests |
| Step 5 (explicit qqq `<version>`s) | None. `qqq-backend-core` and `qqq-middleware-api` are versionless. The only qqq version is the profile's `${qqq.snapshot.version}` BOM import. |
| Secrets | None. The only grep hits are LICENSE boilerplate and README placeholders (`your-signing-secret`) that came from main |
Not merged. The merge decision is James's.
Blocking a user prevents them from interacting with repositories, such as opening or commenting on pull requests or issues. Learn more about blocking a user.
Summary
Re-pins
qbit-webhookstocom.kingsrook:qbit-build-parent:2.0.0(qqq 4.0.0 GA, Java 21) and verifies it on qqq4.1.0-SNAPSHOT, so qqq-all can include it. Before this change develop could not build: it importedqqq-bom-pom:0.40.0-SNAPSHOT, which no longer exists on the Central snapshots repo (itsmaven-metadata.xmlreturns 404).Merge this PR with a merge commit. Do not squash or rebase. The first commit is a
git merge --no-ff origin/mainback-merge. Squashing or rebasing drops main from develop's history, and the next develop-to-main release would hit the same conflicts again.Changes
9c063bfchore: merge main into develop. Main had 8 commits that develop lacked. This brings in main's Java 21 parent (1.5.1), the 0.3.0 release, the Apache-2.0LICENSEandNOTICE, orb 0.5.0,.github/release.ymland the standardized README. Develop keeps itsCODEOWNERSandCLAUDE.md. The conflict resolutions are listed below.d247432build: re-pin to qbit-build-parent 2.0.0 (qqq 4.0.0). Parent 1.5.1 goes to 2.0.0. The child'sqqq-bom-pomimport shadowed the parent's BOM, so it is removed. There were no explicit qqq module<version>s to remove. Three qqq 4.0 API removals broke the build and are migrated:WebhookEventTypeCustomizerInterface.buildEventContentcalled the removedJsonUtils.toJson(Object, Consumer<ObjectMapper>). It now callstoJsonCustomized(postBody, builder -> builder.serializationInclusion(JsonInclude.Include.ALWAYS)), the replacement the 4.0 migration guide gives for builder-level settings. Null fields still appear in webhook post bodies.com.kingsrook.qqq.backend.javalin.QJavalinMetaDatais nowcom.kingsrook.qqq.middleware.javalin.QJavalinMetaData.setAuthentication(...)is nowwithInstanceDefaultAuthentication(...)(same as the B2 re-pin).c8b9853test: assert webhook post body keeps null-valued fields. NewWebhookEventTypeCustomizerInterfaceTest. No existing test covered theALWAYSinclusion that the removed overload provided. Without it, the obvious fix (JsonUtils.toJson(postBody)) would silently drop null fields from every webhook payload.dfeb3fabuild: add opt-in qqq-snapshot profile.-Pqqq-snapshotimportsqqq-bom-pom:${qqq.snapshot.version}(default4.1.0-SNAPSHOT) ahead of the parent's BOM and adds the Central snapshots repository. Default builds still get qqq only from the parent. This is the same profile as the B1 quick-search re-pin.b1df0d6ci: keep package installs unattended on CircleCI. Copies theconfigure-package-installcommand from qbit-quick-search develop and adds it as apre-stepsentry on all 5 jobs. This stops needrestart's interactive prompt from hanginginstall_java(QRun-IO/qqq-orb#11). The orb version stays at main's 0.5.0.bfb0706docs: refresh repo agent notes for the parent 2.0.0 re-pin. InCLAUDE.md, three of the four cautions (branch divergence, the frozen SNAPSHOT, the test-code breaks) no longer apply after this PR. They are replaced with theqqq-snapshotusage and the licensing and README caveats below.Merge conflict resolutions (
9c063bf)LICENSE0cbbff6, 2025-12-25)0e61d75, 2026-01-05)NOTICE, which merged in cleanly. The B2 back-merge resolved it the same way. No license text was edited. Aligning the pom and headers is still an owner decision (see notes)..circleci/config.ymlkingsrook/qqq-orb@0.3.8kingsrook/qqq-orb@0.5.0pom.xmlparentqbit-build-parent1.4.0 (Java 17)pom.xml<properties><properties>to the top,revision0.2.1<properties>below<scm>,revision0.3.0<properties>block. 0.3.0 is already released. On develop the orb turns a stable version into the next minor SNAPSHOT, so this does not republish 0.3.0. I did not pick a next release number.README.mdf8a8e6a)69fc7d0,f346c24)The
qqq-bom-pom0.40.0-SNAPSHOT line merged without a conflict, because main never changed it. The next commit removes it.Related Issues
Closes QRun-IO/qqq#767
Part of QRun-IO/qqq-all#1
Type of Change
Testing
No
mvnwin the repo, so these commands use plainmvn(3.9.16, JDK 21.0.12.1). Test-first: each change was run red before it was made green.curl .../maven-snapshots/com/kingsrook/qqq/qqq-bom-pom/0.40.0-SNAPSHOT/maven-metadata.xml4.1.0-SNAPSHOTreturns 200mvn -B -U verify -Dmaven.repo.local=/tmp/m2-b5Non-resolvable import POM ... qqq-bom-pom:pom:0.40.0-SNAPSHOT (absent)mvn -B clean verifymethod toJson in class JsonUtils cannot be applied to given types(BREAK-04-06)JsonUtils.toJson(postBody)as the migrationtestBuildEventContentKeepsNullValues: null-valued field should be present in the post body(32 tests, 1 failure)toJsonCustomized(... ALWAYS):mvn -B clean verifymvn -B dependency:tree -Pqqq-snapshot -Dincludes=com.kingsrook.qqq:qqq-backend-coreprofile "qqq-snapshot" ... does not exist, resolves 4.0.0 (red)qqq-backend-core,qqq-middleware-api,qqq-middleware-javalinandqqq-openapiall resolve4.1.0-SNAPSHOT. Without-Pthey resolve4.0.0. With-Dqqq.snapshot.version=4.0.0they resolve4.0.0mvn -B help:effective-pom, thenjavap -von a compiled classsource/target21, class file major version 65yqparse of.circleci/config.yml, compared with qbit-quick-search developcommandsblock identical. All 5 jobs havepre-steps: [configure-package-install]Final gates (on
a63a53e, whose tree is identical to HEADbfb0706; the amend only reworded the commit message):mvn -B verify(alsomvn -B clean verify)mvn -B verify -Pqqq-snapshot(alsomvn -B -U clean verify -Pqqq-snapshot)mvn -B -U clean verify -Pqqq-snapshot -Dmaven.repo.local=/tmp/m2-b5qqq-backend-core-4.1.0-20260926.013614-9.jarfromsonatype-snapshotsmvn -B clean verify -Dmaven.repo.local=/tmp/m2-b5I added the isolated-repo gates because the shared
~/.m2can hold locally installed snapshots.mvn installwas not run.mvn test)mvn verify)Checklist
Additional Notes
CI=true" rule has nothing to change here.qqq-orb/mvn_test_onlybuild 35 passed on this branch (workflowtest_only, 1m23s). It got past theinstall_javastep that hung on the needrestart prompt in B2's build 44. I could not runcircleci config validatelocally, because the CLI has no API token to resolve orbs.[WARNING] Failed to build parent project for com.kingsrook.qbits:qbit-webhooks:jar:0.3.0. The publishedqbit-build-parent-2.0.0.pomdeclares its<version>as a literal${revision}because it was not flattened. The effective model is still correct (qqq 4.0.0, Java 21). The fix belongs inqbit-bom.LICENSEandNOTICEsay Apache-2.0, while the pom<licenses>, all 59 Java headers andcheckstyle/license.txtsay AGPL-3.0, and the README footer says "Proprietary". None of these were edited.new WebhooksQBit().configure(...),QWebhookMetaData, HMACX-Webhook-Signaturesigning, and awebhook_logtable. The real entry points areWebhooksQBitProducer,WebhooksRegistryandWebhookEventType, which the old develop README described. The README needs a rewrite against the source.Kingsrookorg URLs in<url>and<scm>.Review, round 1 (plan task B5): approve
Nothing blocks merge. I reviewed HEAD
bfb0706against plan task B5 and the Global Constraints inqqq-all/docs/plan.md.Routing: Deep tier (1329 changed lines across 11 files, mostly LICENSE and README text from main). Risk signals: CircleCI config, and the parent/BOM change in
pom.xml.Blocking
None.
Non-blocking
LICENSEand addingNOTICEchanges develop's LICENSE file, which on its face conflicts with "do not change LICENSE files". Keeping develop's AGPL file would flipmainback to AGPL at the next release. No license text was edited. B2 resolved this the same way and B2's review left it for James, so I'm not blocking on it either.main(0e61d75) as a parent of9c063bf.qbit-build-parent2.0.0 already declarescentral-portal-snapshotswith the same URL. The profile'ssonatype-snapshotsentry adds a second repository, so with-Pqqq-snapshotMaven checks snapshot metadata twice. The plan requires the profile to add the repository, so keep the entry. Using the idcentral-portal-snapshotswould replace the parent's entry instead of duplicating it.0.3.0, which main released onqqq-bom-pom0.27.9. Update the snippet when the next version ships. This comes on top of the README describing APIs that don't exist here, which you already flagged.ALWAYSrestores empty values as well as nulls: bothtoJsondefaults (NON_NULL, then NON_EMPTY) would drop""and empty collections. An assertion with.withValue("lastName", "")would cover that half of the old behavior.${revision}warning belongs inqbit-bom.checkstyle/config.xmlisn't wired into the build (see Verified).Verified (on
bfb0706in the implementer's checkout; nomvn install)mvn -B clean verify(qqq 4.0.0 from the parent)mvn -B clean verify -Pqqq-snapshotdependency:tree -Dincludes=com.kingsrook.qqq, default /-Pqqq-snapshotqqq-backend-core,qqq-middleware-api,qqq-middleware-javalinandqqq-openapiresolve4.0.0/4.1.0-SNAPSHOTjava.version21;WebhookEventTypeCustomizerInterface.classis major version 65toJsonCustomizedmigration (BREAK-04-06)newJsonMapperBuilder()applies the same defaults the removednewObjectMapper()path applied: JavaTimeModule, NON_NULL then NON_EMPTY,FAIL_ON_EMPTY_BEANSoff,WRITE_DATES_AS_TIMESTAMPSoff.serializationInclusion(ALWAYS)then replaces the default inclusion.toJson(Object)now delegates totoJsonCustomized(obj, null).JsonUtils.toJson(postBody).testBuildEventContentKeepsNullValuesfails with "null-valued field should be present in the post body" (32 tests, 1 failure).9c063bforigin/developandorigin/main. Againstmainit differs only inCODEOWNERS,CLAUDE.mdand pom layout/BOM. Main has nosrcchanges that develop lacks, so no source was lost.checkstyle/config.xml, run by hand because the build doesn't wire it in)ClearWebhookSubscriptionHelperMemoizationsTableCustomizer.java. This PR doesn't touch that file, so it predates the PR. Changed and new files: 0 violationsmvn_test_onlybuild 35 is green.mvn_publishuses the samejavaexecutor asmvn_test_onlyin orb 0.5.0, so theconfigure-package-installpre-step applies to the publish jobs the same way. The command block matches qbit-quick-search develop.<version>s)qqq-backend-coreandqqq-middleware-apiare versionless. The only qqq version is the profile's${qqq.snapshot.version}BOM import.your-signing-secret) that came from mainNot merged. The merge decision is James's.