24 Commits
Author SHA1 Message Date
james.maesandClaude Opus 5.5 6af1362668 chore: target QQQ 4.1.0-RC.15 (UI API 1.6.0)
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-15 12:27:06 -05:00
james.maesandClaude Opus 5.5 a9550d82b4 feat(ui-api): extension for QQQ UI API 1.6.0 (operationIds, BasicErrorResponseV1, QQQ 4.1.0-RC.14)
Regenerated against UI API 1.6.0: every operation has an operationId and
the standard error responses (BasicErrorResponseV1: error, code, message,
fieldErrors, traceId); the extension's own error schema is kept but
deprecated; hand-written error bodies go through QJavalinUtils so they carry
code/message/traceId. Extension MINOR bumped.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-15 11:48:12 -05:00
james.maesandClaude Opus 5.5 be27514a58 feat(help): ship the qbit-quick-search help bundle through QQQ help bundles, with coverage gate
Host-neutral help (deployment-specific facts removed), registered with
HelpBundles, gated by HelpBundleAssertions. QQQ 4.1.0-RC.7.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-14 20:37:08 -05:00
james.maesandClaude Opus 5.5 d8cab84387 feat(help): empty-state help for every quick_search table, guardrail and primer tests
Adds help/quick-search.yaml (QQQ help bundle) applied by QuickSearchHelp, a
tables guardrail test, and a seeded-defaults test proving the primer fills
the config tables on a fresh backend.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-14 16:52:39 -05:00
james.maesandClaude Sonnet 5.5 f9d80c1cda docs: typeahead suggest endpoint (README, CHANGELOG, API host notes)
Co-Authored-By: Claude Sonnet 5.5 <noreply@anthropic.com>
2026-10-14 13:14:53 -05:00
james.maes 50bf08f32a Merge remote-tracking branch 'origin/develop' into feature/exclude-classified-fields 2026-10-14 11:13:34 -05:00
james.maesandClaude Opus 5.5 024cda7b08 feat(index): never index PII/PHI-classified or secured fields
Secured-data spec section 15: Quick Search must never put classified
values in the shared search index, snippets or results.

QuickSearchFieldRules decides it in one place on QQQ 4.1.0-RC.6:
isProtected is true for fields classified PII or PHI (field, else the
table's classification), fields with a field security lock, sensitive
fields and masked/password types. Such fields are never indexable;
explicit configs drop them with a warning (never the value); a table
whose primary key is protected (for example a PHI table) is not indexed
at all. Record labels built from protected fields fall back, the
version field is skipped when protected, and results from tables with
protected fields are rebuilt from allowed values only, so documents
indexed before this change cannot leak through labels or snippets.
Tables with only harmless non-indexable fields (booleans, blobs) keep
their labels and are not result-filtered.

A canary test proves a PHI and a PII marker never reach a captured
index request, result or snippet. RC.7 follow-up: also honour core
ClassifiedValues.isClassified (qqq#1182) and FieldAccessHelper lock
tiers (qqq#1172).

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-14 11:13:22 -05:00
james.maesandClaude Opus 5.5 1a4a29fa2b fix(status): count real-time indexing failures
realTimeErrorCount was only ever written as 0, so the status screen,
diagnostics and the status API always said there were no real-time
errors. When the change listener records failed events it now adds
their number to the table's count (after commit, serialized in the JVM,
never failing the user's write). A successful basepull or full reindex
resets it; the field's help text says so.

Fixes #53

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-14 10:42:35 -05:00
james.maesandClaude Opus 5.5 a099a1d1b5 feat(api): QuickSearchStatus API and spec'd GET /qbit/quick-search/status
Hosts no longer need QuickSearchOpenSearchClient or a copy of this qbit's
diagnostics. The new public api package holds:
- QuickSearchStatus, an immutable snapshot: overall state, cluster
  reachability, per-table index counts, backlog, last sync, last error
  and checkedAt;
- QuickSearchStatusService.get(), bounded and cached for five seconds,
  built on the existing diagnostics;
- QuickSearchApi, with the app name and home href, so hosts link to the
  Quick Search home instead of duplicating it.

The same data is served as UI API extension operation
GET /qqq/v1/qbit/quick-search/status (getQuickSearchStatus), gated by the
admin permission. The fragment is committed and checked by a contract
test. The core baseline becomes 4.1.0-RC.6.

Closes #50 (architecture review QRun-IO/qqq#1150, finding F10).

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-13 19:55:28 -05:00
james.maesandClaude Opus 5.5 ee76e1e3c7 fix: record screens of the Quick Search tables, process inputs in Next
- quick_search_index_run and quick_search_failed_event are system
  history: Create and Edit are off (Delete stays), with named sections,
  record labels and pickers (run type and status, event status and
  action, and the table picker for failed events' tableName).
- The index record lists its Recent Runs (read-only child list).
- quick_search_validation_run.reportJson uses a JSON code editor.
- Basepull, Full Reindex and Reconcile are table actions on Quick Search
  Index, not app (nav) children. Their table input is searchTableName
  (tableName still accepted; a Quick Search table name is ignored), so
  running them from the table no longer limits them to quick_search_index.
- Process input steps carry HELP_TEXT and EDIT_FORM components, so the
  Next UI renders their fields; Add Search Index's table input is
  searchTableName for the same reason.
- The Search Diagnostics widget shows the pause, priming and pending
  changes as banners; screenshots under docs/images/data-driven-config.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-12 15:55:24 -05:00
james.maesandClaude Opus 5.5 27a170f208 feat: data-driven Quick Search config, valid create screen, global pause, cancel
The searchable tables and fields are now standard QQQ records:
quick_search_index (the registry; new source and ownerEdited columns) and
the new quick_search_index_field (weight, role, analyzer, snippet and
label flags). Indexing, search, the record-search provider and the
diagnostics read the effective config from them through
QuickSearchConfigStore (30 s cache, dropped by the tables' customizers),
so changes need no redeploy.

Code (@QuickSearchable, searchableTables) only seeds the records:
QuickSearchConfigPrimer adds missing tables and fields at startup and
queues their builds, updates rows nobody edited, never overwrites rows an
admin edited (ownerEdited) and never re-adds what an admin removed
(quick_search_config_tombstone). Basepull builds queued tables
(PENDING_BUILD, BUILDING) and recreates a missing index; an empty index
is rebuilt. Without the config tables the code config is used as before.

The generic Create and Delete of quick_search_index are replaced by
streamed-ETL processes with review steps: Add Search Index (eligible
readable tables only, estimated record count, default fields, sensitive
and hidden fields never indexed), Apply Search Index Changes (previews
field changes, then rebuilds), Disable / Remove Search Index (delete or
keep documents), Cancel Index Run (cooperative flag between batches; runs
end CANCELLED with counts) and Pause / Resume Indexing (global pause in
quick_search_settings; search keeps serving; changes are deferred and
replayed). Field roles shape documents (TITLE label, SUBTITLE, DISPLAY
values, KEYWORD and EXACT outside the snippet) and WORD, ENGLISH and EXACT
analyzers get their own document objects and mapping templates.

Config tables are audited at field level and permission-gated; the
processes need quick_search_admin. Upgrade notes and DDL:
docs/MIGRATION-1.1.md, "Data-driven configuration".

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-12 15:30:47 -05:00
james.maesandClaude Opus 5.5 53416f5163 feat!: snake_case table, PVS and permission names (owner naming rule)
Owner rule (2026-10-12): "table names should always be lowercase and use
_, and qbits should always be prefixed with their name". For 1.1.0-RC.3,
metadata only (the QBit ships no DDL):

- tables: quick_search_index, quick_search_index_run,
  quick_search_failed_event, quick_search_diagnostic_event,
  quick_search_validation_run (were camelCase)
- possible-value sources: quick_search_index, quick_search_searchable_table
- permission bases: quick_search_admin, quick_search_debug; table
  permissions follow the table names
- applyTablePrefix: a host prefix is snake_cased and joined with "_"
  (myApp -> my_app_quick_search_index); processes, widgets and apps keep
  applyPrefix
- @Deprecated LEGACY_* constants keep the old names for upgrade scripts
- docs/MIGRATION-1.1.md: upgrade steps, ALTER TABLE for camelCase
  databases, UPDATEs for audit_table, saved views and reports, permission
  grants; no reindex needed
- QuickSearchTableNamesTest covers new names, legacy constants and prefixing

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-12 14:13:00 -05:00
james.maesandClaude Opus 5.5 e9ec814762 Merge origin/develop into feature/GH-35-search-admin-tooling
Keeps develop's scheduling section and applies its cron rules (cron-capable
scheduler, valid Quartz expression, time zone ID) to validationCronExpression
through one shared check.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-12 06:04:44 -05:00
james.maesandClaude Opus 5.5 b421b4002a feat(#35): search admin tooling (playground, validation, record tools, analyzer, failure diagnostics)
Adds diagnostic tools so every QQQ app using the QBit can validate its
search and debug it from the admin UI. All are table or record actions
(processes with a tableName, never added to the app nav), gated by
permissions, rate-limited per user and tool, recorded as diagnostic events,
and read-only except Reindex in Search.

- Search Playground (Quick Search Index): runs a term as the current user
  the way the global search does; per-table path and reason (OpenSearch
  provider, core database search, not searched), the provider's request
  timed, the global search results, the production query's scores,
  highlights and matched clauses, readable / shown flags, query DSL and raw
  response (unreadable hits redacted), term tokens, explain for a hit.
  The form loops back so a user can search again.
- Validate Search Index (Quick Search Index; schedulable with
  validationCronExpression): cluster and index health, alias, leftover
  physical indexes, live mapping and analysis settings, per-table counts,
  sampled missing / stale documents and orphaned documents, index row status
  and failed events. Reports kept in the new quickSearchValidationRun table.
- Test Search Analyzer: _analyze with the index's analyzers, and whether a
  term's tokens are among a text's indexed tokens.
- Inspect Search Document and Reindex in Search on every indexed table and
  on quickSearchFailedEvent rows: database vs index diff, and why a record
  does or does not match a term (token-level reasons, explain, rank).
- Search failure diagnostics: provider and QuickSearchAction failures are
  recorded as SEARCH_FAILED events (never the term, only its length), with
  runtime counters; the Search Diagnostics widget shows provider state,
  recent failures from every server and the latest validation.
- New settings: enableDiagnosticTools, enableRecordDiagnosticActions,
  validationCronExpression, validationCronTimeZoneId, validationSampleSize,
  diagnosticRunsPerMinute, debugPermissionRules (quickSearchDebug).
- History purge covers the two new tables.

Tests: unit (metadata incl. QInstanceValidator, offline tool behaviour
against a closed port, the playground loop through RunProcessAction) and a
Testcontainers OpenSearch integration test for every tool.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-12 06:02:29 -05:00
james.maesandClaude Opus 5.5 e1771411e1 fix(#29): validate cron zone, warn when basepull is off, document schedulers (#31)
* fix(#29): validate cron zone, warn when basepull is off, document schedulers

A reconcileCronExpression without reconcileCronTimeZoneId passed the
QBit's validation, then QQQ's QInstanceValidator rejected the reconcile
process schedule at startup ("a cron schedule must specify a
cronTimeZoneId"); QQQ has no default zone. validate() now applies the
same checks QQQ does (Quartz CronExpression.validateExpression, a zone
from TimeZone.getAvailableIDs) and requires the zone, naming both
fields. It also rejects a cron on a scheduler in the instance that does
not support cron: QQQ's SimpleScheduler runs fixed intervals only and
fails when the schedule manager starts.

Config-driven tables without their basepull timestamp field now log the
same startup warning as annotated tables, once per table, through one
shared helper. A field set to null is an opt-out and stays quiet.

README and MIGRATION-1.0 document that schedules run only when the host
starts through QApplicationLauncher (qqq-middleware-javalin), and that a
reconcile cron needs a Quartz scheduler and a time zone ID.

Closes #29

* docs(producer): keep develop's hidden-and-password Javadoc after the merge

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>

---------

Co-authored-by: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-12 06:00:59 -05:00
james.maesandClaude Opus 5.5 9c9d2d7e21 docs(#26): fix migration guide and README gaps (#27)
* docs(#26): fix migration guide and README gaps

The migration guide now names the permissions a host must grant and has
a schema section for the 0.x to 1.0 table changes, including the new
quickSearchFailedEvent table and the status value lengths. The
plaintext credentials setter is spelled withAllowPlaintextCredentials,
and the README and PRD point at 1.0.0-RC.2 as the current release.

Closes #26

* docs(#26): name each table in the migration schema step

The schema step now says which table each STRING to TEXT change belongs
to and includes the required, unique quickSearchIndex.tableName, matching
the schema section.

Refs #26

* docs(readme): name 1.1.0-RC.1 as the current release candidate

Maven Central's newest is 1.1.0-RC.1 (develop is the 1.1 line). Keep the
1.0 line's 1.0.0-RC.2 for hosts that stay on 1.0.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>

---------

Co-authored-by: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-12 05:57:02 -05:00
james.maesandClaude Opus 5.5 316ac9484f feat: serve QQQ /qqq/v1/search from OpenSearch (record-search provider)
Implements QQQ core's RecordSearchProviderInterface so the Next UI global
search (POST /qqq/v1/search) is served from the Quick Search index for the
tables this QBit indexes. The provider returns record ids only; core re-reads
them through QueryAction, so table permissions, record security locks and
labels stay core's job. One bounded _msearch per call, with a short backoff
after a failure during which core falls back to its own LIKE search.

Builds against the released QQQ 4.1.0-RC.2, the first release that carries
the SPI (replaces the 4.1.0-SNAPSHOT pin from PR #32).

Re-signed from PR #32 (originally 75dc2b3).

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-11 13:23:22 -05:00
james.maesandClaude Opus 5.5 e06b06000b fix(indexing): real-time inserts index display values (#28)
Real-time INSERT events were indexed from the event's own records, which
carry raw values only, so a foreign key was indexed as its id and the
record label as "2 - email" until the next basepull. Inserts now take the
same path as updates: the listener re-reads the written records inside
the write's transaction with display values and possible-value labels.

A failed re-read is handled as for updates (#13): logged at once, then
recorded in quickSearchFailedEvent after commit for basepull replay,
never failing the host's insert, and nothing recorded on rollback.

Tests: listener unit tests for the PVS label and record label of a
just-inserted record, and for a failed insert re-read (commit and
rollback); an OpenSearch Testcontainers test that a just-inserted
record is searchable by its possible-value label and carries it in
fieldValues and the record label.

Closes #28

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-11 07:54:35 -05:00
james.maes 2b627e767a docs: record the 1.0.0-RC.2 release (#12) (#23)
CHANGELOG gains the RC.2 entry and the #13 to #17 fixes; TODO and SESSION-STATE reflect RC.2 and the remaining GA items.
2026-10-06 21:12:27 -05:00
james.maes 12afb08deb docs: record the 1.0.0-RC.1 release (#12) (#11)
* docs: record the 1.0.0-RC.1 release and the orb RC mechanics

* docs: changelog entry, README version and PRD status for 1.0.0-RC.1

Refs #12.
2026-10-06 15:45:42 -05:00
james.maes 0cd892d1a0 feat: 1.0 release readiness (#10)
* docs: add 1.0 PRD, deep-dive audit, plan, and session state

* build: target qqq 4.1.0-RC.1, opensearch-java 3.10.0, wrapper 3.9.11, real JaCoCo gate

* feat: add 1.0 config surface, instance-scoped runtime, and deprecated static facade

* feat: implement the 4.1 QBit contract with a record-change listener and failed-event capture

* feat: opensearch client with auth modes, TLS, timeouts, mapping v2, aliases, versioned bulk writes

* feat: trustworthy indexing steps and permission-aware bounded search

* docs: rewrite README for 1.0, add CHANGELOG and migration guide

* feat: table picker and result screens for the maintenance processes; close out 1.0 planning docs

* docs: add AGENTS.md so Codex shares the CLAUDE.md guidance

* fix: keep the live index after an alias swap, atomic legacy upgrade, exact paging under record locks

From the PR #10 review: never delete the new physical index once the alias points at it, upgrade a legacy concrete index in one aliases request, base hasMore on raw hits so filtered pages stay reachable, and deduplicate requested table names.

* docs: explain the hasMore trade-off under record locks

* docs: session state for the RC.1 hand-off
2026-10-05 09:50:05 -05:00
james.maes 24c0da4f63 chore: clean slate for v1 clean-room rewrite
Remove all existing Java source and test files. Remove template docs.
Remove unused javalin dependency. Raise JaCoCo thresholds to 70%/90%.
2026-03-28 15:14:30 -05:00
james.maes 23f4305f1b feat(tests): add tests for Step and Producer classes
- Add FullReindexStepTest with 7 tests
- Add BasepullIndexStepTest with 8 tests
- Add QuickSearchQBitProducerTest with 12 tests
- Update CLAUDE.md with project status
- Add docs/TODO.md and docs/SESSION-STATE.md
2026-01-09 14:54:31 -06:00
james.maes ffefd8f148 Initial commit 2026-01-09 11:56:58 -06:00