Closes#9: guardrails G1, G2, G3, G4 and G7 from architecture review QRun-IO/qqq#1150, section 5. G5 (no HTML widgets) waits for the core typed widgets (qqq#1151).
Shape. The repo becomes a two-module reactor:
com.kingsrook:qbit-build-parent, the existing pom, now at 2.1.0;
com.kingsrook:qbit-guardrails, a new jar. It has no parent, which avoids a cycle. It depends on ArchUnit core, the JUnit Jupiter API and maven-artifact, and does not depend on QQQ.
A parent profile qbit-guardrails is active unless -Dqbit.guardrails.skip=true. It adds the jar in test scope and runs its *GuardrailTest classes inside each qbit's surefire run through dependenciesToScan, with configuration merged, not overridden. It also runs the enforcer at validate.
Rollout without breakage. Every rule only reports by default: the enforcer has fail=false, and the tests pass while printing [qbit-guardrails] Gx FAIL (report-only) lines. A qbit opts in with <qbit.guardrails.enforce>true</qbit.guardrails.enforce> and, for G2, <qbit.guardrails.strictUiApiRoutes>true</qbit.guardrails.strictUiApiRoutes>.
Rule
Check
G1-deps
Enforcer bannedDependencies: com.kingsrook.qbits:* in compile/runtime/provided/system scope (direct and transitive) is banned, except qbit-object-storage and *-api artifacts.
G1-arch
ArchUnit: main classes in com.kingsrook.qbits.<own>.. depend only on their own packages, com.kingsrook.qqq.., the JDK, libraries and allowed com.kingsrook.qbits.<y>.api...
G1-literals
Source scan: string literals naming another qbit's classes, e.g. Class.forName("com.kingsrook.qbits.alerts..."). Reports file and line.
G2
Forwards qqq.uiApi.strictRoutes=true to tests; core reads it on RC.6+. The default flips with core RC.7 (qqq#1159).
G3
If main code references MiddlewareSpecExtension, the qbit needs a committed openapi/qqq-v1/*.yaml and a test that uses UiApiContract/UiApiContractAssertions. Raw /qqq/v1 route literals are always reported as warnings.
G4
STAND-IN(core=OWNER/REPO#N, removeBy=VERSION): fails when the core version on the classpath (qqq-backend-core pom.properties) is at or past removeBy (ComparableVersion), or when a marker is malformed. Untagged "stand-in" prose is listed as a warning.
G7
Enforcer requireReleaseDeps for com.kingsrook.qqq:* and com.kingsrook.qbits:*. The project's own SNAPSHOT revision is fine.
The README has the full grammar, properties and opt-in instructions.
What fails today (enforce=true, against each qbit's develop)
qbit
Fails
qbit-easypost-tracking
G1-deps (qbit-alerts), G1-arch (17 edges into alerts internals), G1-literals ("...AlertsRuntime"). Fixed by qbit-easypost-tracking#16.
qbit-marketing
G1-deps (qbit-assistant), G1-arch (180 edges into assistant internals). Waits for qbit-assistant-provider-api (assistant#30, then marketing#9).
Raw /qqq/v1 route warnings: alerts (/alerts), sql-workbench (/sql-workbench) and assistant (/assistant). The first two are fixed by qbit-alerts' extension PR and qbit-sql-workbench#21.
A consumer smoke test in report and enforce modes.
A fleet trial over the 16 qbits: 80 guardrail tests, with the 4 failing qbits listed above.
Consumer note: the trials used Maven 3.9.11, because Maven 3.10.0 hits an existing ${revision} parent-model error in consumers.
After merge
The release captain publishes qbit-build-parent 2.1.0 and qbit-guardrails 2.1.0 together. Qbits then move their parent to 2.1.0; the passing ones can set qbit.guardrails.enforce=true straight away.
## Summary
Closes #9: guardrails G1, G2, G3, G4 and G7 from architecture review QRun-IO/qqq#1150, section 5. G5 (no HTML widgets) waits for the core typed widgets (qqq#1151).
**Shape.** The repo becomes a two-module reactor:
- `com.kingsrook:qbit-build-parent`, the existing pom, now at 2.1.0;
- `com.kingsrook:qbit-guardrails`, a new jar. It has no parent, which avoids a cycle. It depends on ArchUnit core, the JUnit Jupiter API and maven-artifact, and does not depend on QQQ.
A parent profile `qbit-guardrails` is active unless `-Dqbit.guardrails.skip=true`. It adds the jar in test scope and runs its `*GuardrailTest` classes inside each qbit's surefire run through `dependenciesToScan`, with configuration merged, not overridden. It also runs the enforcer at `validate`.
**Rollout without breakage.** Every rule **only reports by default**: the enforcer has `fail=false`, and the tests pass while printing `[qbit-guardrails] Gx FAIL (report-only)` lines. A qbit opts in with `<qbit.guardrails.enforce>true</qbit.guardrails.enforce>` and, for G2, `<qbit.guardrails.strictUiApiRoutes>true</qbit.guardrails.strictUiApiRoutes>`.
| Rule | Check |
|---|---|
| G1-deps | Enforcer `bannedDependencies`: `com.kingsrook.qbits:*` in compile/runtime/provided/system scope (direct and transitive) is banned, except `qbit-object-storage` and `*-api` artifacts. |
| G1-arch | ArchUnit: main classes in `com.kingsrook.qbits.<own>..` depend only on their own packages, `com.kingsrook.qqq..`, the JDK, libraries and allowed `com.kingsrook.qbits.<y>.api..`. |
| G1-literals | Source scan: string literals naming another qbit's classes, e.g. `Class.forName("com.kingsrook.qbits.alerts...")`. Reports file and line. |
| G2 | Forwards `qqq.uiApi.strictRoutes=true` to tests; core reads it on RC.6+. The default flips with core RC.7 (qqq#1159). |
| G3 | If main code references `MiddlewareSpecExtension`, the qbit needs a committed `openapi/qqq-v1/*.yaml` and a test that uses `UiApiContract`/`UiApiContractAssertions`. Raw `/qqq/v1` route literals are always reported as warnings. |
| G4 | `STAND-IN(core=OWNER/REPO#N, removeBy=VERSION)`: fails when the core version on the classpath (`qqq-backend-core` pom.properties) is at or past removeBy (ComparableVersion), or when a marker is malformed. Untagged "stand-in" prose is listed as a warning. |
| G7 | Enforcer `requireReleaseDeps` for `com.kingsrook.qqq:*` and `com.kingsrook.qbits:*`. The project's own SNAPSHOT revision is fine. |
The README has the full grammar, properties and opt-in instructions.
## What fails today (enforce=true, against each qbit's `develop`)
| qbit | Fails |
|---|---|
| qbit-easypost-tracking | G1-deps (qbit-alerts), G1-arch (17 edges into alerts internals), G1-literals (`"...AlertsRuntime"`). **Fixed by qbit-easypost-tracking#16.** |
| qbit-marketing | G1-deps (qbit-assistant), G1-arch (180 edges into assistant internals). Waits for `qbit-assistant-provider-api` (assistant#30, then marketing#9). |
| qbit-assistant | G1-deps (provider modules), G7 (assistant SNAPSHOT dependencies). |
| qbit-secured-data | G7 (QQQ `4.1.0-SNAPSHOT`; secured-data#4). |
| qbit-collaboration | G1-arch: 1 edge, `StorageBucketConfig.getMaxUploadBytes()` from object-storage's non-`api` package. |
| alerts, sql-workbench, backups, object-storage, cdn, webhooks, quick-search, authentik, data-analysis, web-performance, cms | pass |
Raw `/qqq/v1` route warnings: alerts (`/alerts`), sql-workbench (`/sql-workbench`) and assistant (`/assistant`). The first two are fixed by qbit-alerts' extension PR and qbit-sql-workbench#21.
## Verification
- `mvn -B verify` and `install`: 17 unit tests.
- 18 enforcer integration cases (wildcards, scopes, optional deps, SNAPSHOT parents).
- A consumer smoke test in report and enforce modes.
- A fleet trial over the 16 qbits: 80 guardrail tests, with the 4 failing qbits listed above.
Consumer note: the trials used Maven 3.9.11, because Maven 3.10.0 hits an existing `${revision}` parent-model error in consumers.
## After merge
The release captain publishes `qbit-build-parent` 2.1.0 and `qbit-guardrails` 2.1.0 together. Qbits then move their parent to 2.1.0; the passing ones can set `qbit.guardrails.enforce=true` straight away.
🤖 Generated with [Claude Code](https://claude.com/claude-code)
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
Closes #9: guardrails G1, G2, G3, G4 and G7 from architecture review QRun-IO/qqq#1150, section 5. G5 (no HTML widgets) waits for the core typed widgets (qqq#1151).
Shape. The repo becomes a two-module reactor:
com.kingsrook:qbit-build-parent, the existing pom, now at 2.1.0;com.kingsrook:qbit-guardrails, a new jar. It has no parent, which avoids a cycle. It depends on ArchUnit core, the JUnit Jupiter API and maven-artifact, and does not depend on QQQ.A parent profile
qbit-guardrailsis active unless-Dqbit.guardrails.skip=true. It adds the jar in test scope and runs its*GuardrailTestclasses inside each qbit's surefire run throughdependenciesToScan, with configuration merged, not overridden. It also runs the enforcer atvalidate.Rollout without breakage. Every rule only reports by default: the enforcer has
fail=false, and the tests pass while printing[qbit-guardrails] Gx FAIL (report-only)lines. A qbit opts in with<qbit.guardrails.enforce>true</qbit.guardrails.enforce>and, for G2,<qbit.guardrails.strictUiApiRoutes>true</qbit.guardrails.strictUiApiRoutes>.bannedDependencies:com.kingsrook.qbits:*in compile/runtime/provided/system scope (direct and transitive) is banned, exceptqbit-object-storageand*-apiartifacts.com.kingsrook.qbits.<own>..depend only on their own packages,com.kingsrook.qqq.., the JDK, libraries and allowedcom.kingsrook.qbits.<y>.api...Class.forName("com.kingsrook.qbits.alerts..."). Reports file and line.qqq.uiApi.strictRoutes=trueto tests; core reads it on RC.6+. The default flips with core RC.7 (qqq#1159).MiddlewareSpecExtension, the qbit needs a committedopenapi/qqq-v1/*.yamland a test that usesUiApiContract/UiApiContractAssertions. Raw/qqq/v1route literals are always reported as warnings.STAND-IN(core=OWNER/REPO#N, removeBy=VERSION): fails when the core version on the classpath (qqq-backend-corepom.properties) is at or past removeBy (ComparableVersion), or when a marker is malformed. Untagged "stand-in" prose is listed as a warning.requireReleaseDepsforcom.kingsrook.qqq:*andcom.kingsrook.qbits:*. The project's own SNAPSHOT revision is fine.The README has the full grammar, properties and opt-in instructions.
What fails today (enforce=true, against each qbit's
develop)"...AlertsRuntime"). Fixed by qbit-easypost-tracking#16.qbit-assistant-provider-api(assistant#30, then marketing#9).4.1.0-SNAPSHOT; secured-data#4).StorageBucketConfig.getMaxUploadBytes()from object-storage's non-apipackage.Raw
/qqq/v1route warnings: alerts (/alerts), sql-workbench (/sql-workbench) and assistant (/assistant). The first two are fixed by qbit-alerts' extension PR and qbit-sql-workbench#21.Verification
mvn -B verifyandinstall: 17 unit tests.Consumer note: the trials used Maven 3.9.11, because Maven 3.10.0 hits an existing
${revision}parent-model error in consumers.After merge
The release captain publishes
qbit-build-parent2.1.0 andqbit-guardrails2.1.0 together. Qbits then move their parent to 2.1.0; the passing ones can setqbit.guardrails.enforce=truestraight away.🤖 Generated with Claude Code