Found while installing 0.1.0-RC.2 into kof22 (KofTwentyTwo/Website-Backend).
Virtual fields
SecuredDataClassifications.effective(table, fieldName) used QTableMetaData.getField, which throws for a virtual (computed) field. Core resolves every field the table exposes, virtual ones included, so any non-system read of a table with a virtual field failed (Field [sitePath] was not found in table [cms_author]), including the QQQ API middleware's instance validation. Virtual fields now fall back to the table classification (unclassified table: FULL).
Access-log worker
The worker called flush() every interval, and flush() always opened a QContext. QContext.init validates an unvalidated instance, and the worker starts at produce() time while the host is still building the instance, so:
the first tick validated and enriched a half-built instance on the worker thread, racing the host's main thread; and
when that validation failed, the exception cancelled the scheduled task permanently (scheduleWithFixedDelay suppresses later runs), so no VIEW or exact-match row was ever written.
Idle ticks now return before opening a context, and the scheduled tick catches and logs (value-free) a failed drain so the next tick retries. The qbit's own tests did not see this because they use a 60 s interval.
Not in this PR (worth a follow-up): the logger's JVM shutdown hook holds every instance strongly (a test suite that builds many instances keeps them all; qbit-assistant#57 solved the same with an opt-out).
Found while installing 0.1.0-RC.2 into kof22 (KofTwentyTwo/Website-Backend).
## Virtual fields
`SecuredDataClassifications.effective(table, fieldName)` used `QTableMetaData.getField`, which throws for a virtual (computed) field. Core resolves every field the table exposes, virtual ones included, so any non-system read of a table with a virtual field failed (`Field [sitePath] was not found in table [cms_author]`), including the QQQ API middleware's instance validation. Virtual fields now fall back to the table classification (unclassified table: FULL).
## Access-log worker
The worker called `flush()` every interval, and `flush()` always opened a `QContext`. `QContext.init` validates an unvalidated instance, and the worker starts at `produce()` time while the host is still building the instance, so:
- the first tick validated and enriched a half-built instance on the worker thread, racing the host's main thread; and
- when that validation failed, the exception cancelled the scheduled task permanently (`scheduleWithFixedDelay` suppresses later runs), so no VIEW or exact-match row was ever written.
Idle ticks now return before opening a context, and the scheduled tick catches and logs (value-free) a failed drain so the next tick retries. The qbit's own tests did not see this because they use a 60 s interval.
## Tests
- `SecuredDataResolverTest.virtualFieldsTakeTheTableClassification`
- `SecuredDataAccessLoggerTest.idleTicksNeverValidateAnUnfinishedInstance`
`./mvnw verify` passes locally (checkstyle, JaCoCo).
Not in this PR (worth a follow-up): the logger's JVM shutdown hook holds every instance strongly (a test suite that builds many instances keeps them all; qbit-assistant#57 solved the same with an opt-out).
🤖 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.
Found while installing 0.1.0-RC.2 into kof22 (KofTwentyTwo/Website-Backend).
Virtual fields
SecuredDataClassifications.effective(table, fieldName)usedQTableMetaData.getField, which throws for a virtual (computed) field. Core resolves every field the table exposes, virtual ones included, so any non-system read of a table with a virtual field failed (Field [sitePath] was not found in table [cms_author]), including the QQQ API middleware's instance validation. Virtual fields now fall back to the table classification (unclassified table: FULL).Access-log worker
The worker called
flush()every interval, andflush()always opened aQContext.QContext.initvalidates an unvalidated instance, and the worker starts atproduce()time while the host is still building the instance, so:scheduleWithFixedDelaysuppresses later runs), so no VIEW or exact-match row was ever written.Idle ticks now return before opening a context, and the scheduled tick catches and logs (value-free) a failed drain so the next tick retries. The qbit's own tests did not see this because they use a 60 s interval.
Tests
SecuredDataResolverTest.virtualFieldsTakeTheTableClassificationSecuredDataAccessLoggerTest.idleTicksNeverValidateAnUnfinishedInstance./mvnw verifypasses locally (checkstyle, JaCoCo).Not in this PR (worth a follow-up): the logger's JVM shutdown hook holds every instance strongly (a test suite that builds many instances keeps them all; qbit-assistant#57 solved the same with an opt-out).
🤖 Generated with Claude Code