ParentRowCustomizer and AttachmentPreviewCustomizer read the parent of
every child row to decide whether the row is visible, so rows that share a
parent repeated the same read: 61 tasks on two parents ran 61 parent reads
(TagLookupStatementBudgetTest), now 2. The answer is kept only for the one
postQuery call, where the session and transaction cannot change.
Also correct the README: the Tags field is selectable.
Co-Authored-By: Claude Fable 5.2 <noreply@anthropic.com>
Core selects the Tags virtual field for every query or get that names no
fields, so every such read of a TAGS-enabled table ran one
collaboration_record_tag statement, including reads made one record at a
time and tables that were never tagged. Measured with 600 added parents
(TagLookupStatementBudgetTest): a 602-row list ran 2 statements, 600 gets
ran 600 and 100 single-record updates ran 100; now 1 probe, then 0.
TaggedTables remembers, per node and per table, whether any assignment
exists (core Memoization registered with ClusterCaches, 30 s by default,
withTagLookupCacheSeconds, 0 disables). Writing an assignment clears it at
once and after commit and publishes memo-invalidate. The probe ignores row
locks and returns no data, so the answer is the same for every session.
Tables with assignments are read as before: one lookup per 500 parents.
Co-Authored-By: Claude Fable 5.2 <noreply@anthropic.com>
The PostgreSQL 17 journal qualification refused every call on Amazon Aurora
PostgreSQL, which reports full_page_writes=off and does not expose the
parameter in its cluster parameter group.
- The settings query gains an eleventh column,
to_regprocedure('aurora_version()') IS NOT NULL, which is true on Aurora,
false on stock PostgreSQL 17 and never raises.
- full_page_writes=off is accepted only when that column is true. Stock
PostgreSQL with full_page_writes=off is still refused, and synchronous_commit,
fsync, recovery, isolation, time zone and lock timeout are unchanged on Aurora.
- The decision is the pure function tornPagesPrevented, covered by an always-on
unit test. The opt-in real PostgreSQL 17 suite simulates Aurora with a
function named aurora_version() and full_page_writes=off on the server.
Co-Authored-By: Claude Fable 5.2 <noreply@anthropic.com>
Bounded STRING custom fields now carry QQQ's native ValueTooLongBehavior.ERROR,
so a write longer than maxLength is rejected with a field error and the stored
value is preserved. The QQQ default is PASS_THROUGH, which let over-long values
through unchecked.
After the runtime metadata overlay refreshes (immediately, or in the after-commit
callback of the originating transaction), persisted help content is reloaded so
sections and fields created by publishing a definition receive their stored
helpContent rows without a restart.
Two CustomFieldsTest regressions cover invalid-write preservation and stored
help on a newly published section. Carried locally by GG Forge as
collaboration-custom-fields.patch (supersedes collaboration-field-length.patch).
Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
Signed-off-by: James Maes <james@kof22.com>
Replace the default-active Core snapshot profile with the released Core
4.1.0-RC.22 BOM, pin qbit-redis to its first release candidate, and replace
the snapshot-era status text with the current 1.0 status.
Co-Authored-By: Claude Sonnet 5.5 <noreply@anthropic.com>
Replace the platform-ci release callers with one caller of
KofTwentyTwo/ci-workflows maven.yaml (qqq-orb port).
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Fixes#54. Each merged viewer carries connections; GET /presence?perConnection=true adds one lease per connection with opaque ids; presence.changed carries leaseCount and leases.
Security: the raw session uuid is never stored, replicated or returned. The heartbeat uses an HMAC-SHA256 derived sessionId keyed by QQQ_PRESENCE_SESSION_SECRET (random per-process secret when unset, so set it on every pod). The public leases() accessor and Lease record are removed. 162 tests green, including a test that the uuid is absent from every bus message and snapshot.
Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
The session uuid is a bearer credential (clients resume sessions with
it as a cookie or bearer token). The first cut kept it on the lease,
replicated it on the internal presence bus and exposed it through a
public leases() accessor.
Derive an opaque session id instead: "s" + the first 16 hex digits of
HMAC-SHA256(secret, sessionUuid), with QQQ_PRESENCE_SESSION_SECRET
(shared across pods) or a random per-process secret. The raw uuid is
dropped at heartbeat; only the opaque id is stored, replicated and
returned (per-connection viewers and presence.changed leases). The
leases() accessor and Lease record are removed. Tests assert the raw
uuid never appears in bus messages, snapshots or HTTP responses.
Refs #54
Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
Two terminals or tabs of one user collapsed into a single viewer, so
clients could not tell "Admin, twice" from "Admin". The registry
already kept one lease per connection; expose it additively:
- merged viewers carry connections (live leases behind the viewer)
- snapshots and presence.changed carry leaseCount
- GET /presence?perConnection=true adds leases, one viewer per lease
with an opaque connection id, mode, field and lease start;
presence.changed always carries leases (subscribers cannot opt in)
- leases record the server-side session uuid, never sent to clients
- counts() still counts users; connectionCounts() counts leases and
the Who's here card shows both
Extension collaboration 1.2.0; regenerated committed document.
Fixes#54
Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
The presence payload omitted viewers once the last viewer left, so clients kept showing the previous list. Viewers are now always serialized, as an empty list after the last leave.
Fixes#52
Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
QRun-IO/.github#2 adds a tag-and-release job that signs vX with the
QRUN_TAG_SIGNING_KEY secret after Maven Central confirms the artifact.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
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>
The producer read a literal version, so released artifacts reported a stale
or -SNAPSHOT version. qbit-collaboration.properties (version=${project.version}) is
Maven-filtered and the producer loads it; tests assert producer == bundle ==
artifact version.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
The bundle version is now Maven-filtered, so a literal SNAPSHOT in the test
fails every release build. Surefire passes the artifact version in
qbit.artifactVersion.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
On release branches the bundle's version and the producer's differed, so the
bundle test that asserts they match failed, and released bundles recorded
SNAPSHOT as their provenance. help/*.yaml is now filtered:
version: ${project.version}.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
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>
CollaborationFollowersProvider implements core RecordFollowersProviderInterface
over collaboration_follow (CHANGE/ALERT -> notifyOnChange, COMMENT ->
notifyOnComment, muted excluded), registered via RecordFeaturesMetaData, so
qbit-alerts can notify followers. Cached favorite/recent labels are stored
only when FieldAccessHelper.isRecordLabelFullAccess holds; notification
titles fall back to the table label and id for recipients without FULL
label access. Timeline masking (MASKED shows the mask, HIDDEN omitted,
canary never leaks) is covered by tests.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
DeleteAttachmentsProcess Review/Delete create their AttachmentsService on
first use instead of in the constructor, so QQQ can build them during
context-free instance validation (fixes#20). Home table columns no
longer set widths, so narrow cards wrap instead of scrolling (fixes#21).
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Moves the optional email integration from qbit-email internals to
com.kingsrook.qbits.email.api (qbit-email 0.1.0-RC.2) with identical
behaviour, and moves to qbit-build-parent 2.1.1, whose default G1
allow-lists include qbit-email's API (QRun-IO/qbit-bom#12), so the
per-repo allow-lists are gone.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Moves qqq-orb@0.6.8 to qqq-orb@0.6.9, which passes --update-snapshots so cached SNAPSHOT dependencies are re-checked on every build (QRun-IO/qqq-orb#22).
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
AttachmentStorage.maxBytes now reads StorageBucket.maxUploadBytes() from
the api package (null = no bucket limit); uploads stop at the smaller of
the bucket cap and the per-table cap.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Keeps develop's changes and re-applies the optional qbit-email 0.1.0-RC.1
delivery on top (adapted to develop's refactors; guardrail allow-lists
for the optional integration where the build parent enforces them).
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Moves from 0.1.0-SNAPSHOT to the first release candidate. Test schemas
gain the RC's email_provider_sender_address table and the message
from_address_id column where they copy the qbit's DDL.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Core's "Record was Edited/Inserted/Deleted" audit messages become "Edited by
Alice Morgan: Title, Total", "Created by Alice Morgan" and "Deleted by ...",
using personalized field labels (first four, then "and N more"). Detail rows
and unrecognized messages are untouched.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
A ?user= request parameter (remembered in the collab_dev_user cookie) chooses the
demo identity for that browser, falling back to -Dcollab.dev.user. The session
now carries values.user (name and email) so the frontend shows Alice Morgan
instead of Anonymous. README documents opening a second browser to see presence.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
People search (share recipients and mention candidates) never returns the
current user. The record link already honors frontendBaseUrl; the demo server
now sets it to http://localhost:<port> and the test asserts the absolute URL.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Tasks, attachments, comments' revisions, followers, shares, access requests and
assignments gain additive *DisplayName properties, resolved with one directory
query per response (unknown ids fall back to the id). User-id fields on the
collaboration tables use the collaboration_user possible value source, keyed on
userId, so admin screens show names. Contract regenerated.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Replace the HTML wall of text with a KPI block (comments this week, open
threads, open and overdue tasks, open access requests, attachments with a
human-readable size, active custom fields) and full-width Tags, Custom fields
and Who's here tables, each with a real empty state and no hard-coded colors.
Counts still use the caller's session. Tests updated to assert widget data.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Phase (d) of QRun-IO/qbit-email docs/DESIGN.md section 11. qbit-email is an
optional dependency (0.1.0-SNAPSHOT until its first RC) used through one
adapter class behind a classpath and instance guard; without it the
existing behaviour is unchanged.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
The producer already nests the app under its parent, or leaves it top-level.
QQQ builds the appTree from apps whose parents survive permission checks, so a
denied parent silently orphaned Collaboration (present in apps, absent from the
tree). The demo granted admin.access while the Admin app requires
admin.hasAccess. Tests assert the tree for both placements and for the demo.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
TagCriteriaExpander treated non-Number values as names, so ids arriving
as strings from v1 JSON matched nothing. Numbers and numeric strings that
match an existing tag are ids; everything else is a normalized name.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
CollaborationDevServer serves the Next dashboard over an in-memory
instance (people, orders, every record feature on, seeded comments,
files, tags, custom fields, tasks, shares, audit history) with mock users
alice, bob and admin. CustomFieldsProvider now registers generated SELECT
possible value sources on refresh.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
collaboration_field_definition (with DataClassification) and
collaboration_field_value; CustomFieldsProvider contributes ACTIVE fields
through the runtime overlay with JsonPathStorage (Postgres, memory) or a
read-only EAV virtual field elsewhere; Publish/Archive/Rebuild index
actions; the collaborationBuildFieldIndexes job creates expression
indexes CONCURRENTLY (or records the DDL when unmanaged). Presence:
PresenceRegistry replicated over the realtime bus with a 45 s TTL and a
cap, presence.changed events, and POST/DELETE/GET /presence endpoints;
an admin Who's here widget.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Feature settings drive core RecordFeaturesMetaData through
RecordFeatureOverrideProviderInterface; feature permissions come from core
(policy-editor catalog); extension tables declare RecordReferenceTableMetaData
so core enforces parent read access, write rules and mirrored locks; core
cleanup, record-access and summary SPIs are implemented; tags become the
collaborationTags virtual field with a QSubQuery criteria expander (filter,
quick search, #tag) contributed through the runtime overlay; qbit events
publish on the realtime bus. Removes the stand-ins (OtherUserAccess,
UserSessionSupplier, ParentTableRestriction, TagFilterTranslator). Adds
the link, share, access request, task, assignment and My work endpoints.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
parentCommentId lists only its thread (threadId or id = ${input.threadId});
feature settings' scopeName is listed by scopeType (apps or eligible
tables) through FeatureScopeNamePossibleValueProvider; a comment's
fieldName lists the visible fields of its tableName through the reusable
RecordFieldNamePossibleValueProvider. TODOs mark the core dependsOn
switch. One test per pair.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Stop reading the bucket cap through StorageBucket.config() (the non-api
StorageBucketConfig, flagged by the qbit-bom guardrails, G1-arch).
Collaboration enforces its per-table cap; object storage still enforces
the bucket cap at put time. An api accessor is requested upstream.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
collaboration_share and collaboration_access_request (requesters see only
table and id; non-readers get a notification without record data);
collaboration_task with reminders and overdue notices, Mark done/Reopen;
collaboration_record_assignment with Assign to / Take ownership bulk
actions; MyWorkService summary (open tasks, records assigned, unread
mentions); home cards; parent cleanup.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
31 spec'd operations over favorites, recents, attachments (multipart
upload; download/preview return a short-lived url), tags, comments,
mentions, follow, timeline and people, with Collaboration* typed schemas,
the committed extension fragment openapi/qqq-v1/collaboration.yaml and a
contract test. Built against QQQ 4.1.0-SNAPSHOT for the extension point
(QRun-IO/qqq#1128); moves to 4.1.0-RC.6 when released.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
collaboration_tag and collaboration_record_tag; FREE or APPROVED_ONLY per
table from feature settings; normalized names, suggestions, usage counts;
Approve, Merge and Delete tags record actions; Add tags / Remove tags bulk
actions for configured host tables; TagFilterTranslator for the filter
operators, to be wrapped in core's criteria expander once QRun-IO/qqq#1140
is released; tag activity on the timeline; the home Tags card.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
collaboration_comment (+ revisions, mentions), collaboration_follow,
collaboration_activity and collaboration_follow_digest tables; comment
threads with markdown bodies, @mentions checked for the recipient's
access, edit history, soft delete and resolve; follow with defaults,
bulk Follow/Unfollow actions and batched change notifications; a
timeline merging audit and activity; a people directory. Notifications go
through a CollaborationNotifier (no-op by default) until the
QAlerts.notify adapter (QRun-IO/qqq#1125) is available.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
collaboration_attachment rows per record, uploads streamed into the
configured bucket with size limits and sha256, presigned downloads and
FILE_PREVIEW previews, a streamed-ETL Delete attachments action, parent
cleanup, the home card, and a parent-access stand-in for core's
record-reference security (QRun-IO/qqq#1138) to be replaced when released.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
collaboration_favorite and collaboration_recent tables locked to their
owner (admin all-access), FavoritesService (star, unstar, list with label
refresh and Unavailable entries, reorder) and RecentsService (throttled
view recording, list, hidden daily trim). Home shows real counts.
UI API endpoints follow once QRun-IO/qqq#1128 is released.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Scaffold per QRun-IO/qqq docs/QRUN-QBIT-REPO-STANDARD.md (CircleCI orb
0.6.8 workflows, release-rc caller, dependabot, wrapper, checkstyle,
community pointer files). First qbit code: CollaborationQBitProducer,
collaboration_feature_setting and collaboration_user tables, the
Collaboration app home with real counts, and the Postgres DDL script.
Design: QRun-IO/qqq#1136.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>