Phases (f) and (g), qbit side, of QRun-IO/qqq#1136 (spec §4 rows 8 and 9, §6.3, §9.2): the custom field designer and presence. Stacked on #8. It builds against the QQQ develop SNAPSHOT and moves to RC.6 together with #8, so it stays a draft until then.
Custom fields:
Tables:collaboration_field_definition ("Custom Fields", audited) with a classification (PUBLIC, CONFIDENTIAL, PII or PHI), plus collaboration_field_value, the EAV fallback.
CustomFieldsProvider, a RuntimeMetaDataProviderInterface, contributes each ACTIVE definition as a normal field cf_<name>:
type, label, required, help text, section, search field, and the AiFieldMetaData classification;
stored with JsonPathStorage("qqq_custom_fields", name) on PostgreSQL and the memory backend, after a one-time check that the column exists;
on other backends, a read-only EAV virtual field that can be filtered through a criteria expander.
Actions:Publish field, Archive field (its Review counts the affected values) and Rebuild index. Type changes are allowed only in DRAFT.
Indexes:collaborationBuildFieldIndexes creates expression indexes with CREATE INDEX ... CONCURRENTLY, tracking BUILDING, READY or FAILED. With withManagedIndexes(false) it only records the DDL.
CollaborationSchema.customFieldsDdl(table) prints the one-time column and GIN index.
Presence:
PresenceRegistry is in memory and replicated across pods over the core realtime bus, on the internal instance:presence channel. Entries expire after 45 s, are capped at 50,000, and one user's tabs are merged.
presence.changed is published to record: and table: channels.
Endpoints: POST, DELETE and GET /qqq/v1/qbit/collaboration/presence. Each re-checks access and needs PRESENCE enabled.
Nothing is persisted, as the owner decided. Admins get a read-only "Who's here" widget.
Limitations:
EAV fields are read-only for now.
Multi-select uses JSON arrays without native array operators.
Other pods pick up definition changes through the overlay's generation refresh.
Test plan
./mvnw -o verify against a fresh develop SNAPSHOT: 113 tests, checkstyle clean, coverage 96.1% instructions and 99.1% classes.
## Summary
Phases (f) and (g), qbit side, of QRun-IO/qqq#1136 (spec §4 rows 8 and 9, §6.3, §9.2): the custom field designer and presence. **Stacked on #8.** It builds against the QQQ develop SNAPSHOT and moves to RC.6 together with #8, so it stays a draft until then.
**Custom fields:**
- **Tables:** `collaboration_field_definition` ("Custom Fields", audited) with a `classification` (PUBLIC, CONFIDENTIAL, PII or PHI), plus `collaboration_field_value`, the EAV fallback.
- **`CustomFieldsProvider`**, a `RuntimeMetaDataProviderInterface`, contributes each ACTIVE definition as a normal field `cf_<name>`:
- type, label, required, help text, section, search field, and the `AiFieldMetaData` classification;
- stored with `JsonPathStorage("qqq_custom_fields", name)` on PostgreSQL and the memory backend, after a one-time check that the column exists;
- on other backends, a read-only EAV virtual field that can be filtered through a criteria expander.
- **Actions:** **Publish field**, **Archive field** (its Review counts the affected values) and **Rebuild index**. Type changes are allowed only in DRAFT.
- **Indexes:** `collaborationBuildFieldIndexes` creates expression indexes with `CREATE INDEX ... CONCURRENTLY`, tracking BUILDING, READY or FAILED. With `withManagedIndexes(false)` it only records the DDL.
- `CollaborationSchema.customFieldsDdl(table)` prints the one-time column and GIN index.
**Presence:**
- `PresenceRegistry` is in memory and replicated across pods over the core realtime bus, on the internal `instance:presence` channel. Entries expire after 45 s, are capped at 50,000, and one user's tabs are merged.
- `presence.changed` is published to `record:` and `table:` channels.
- Endpoints: `POST`, `DELETE` and `GET /qqq/v1/qbit/collaboration/presence`. Each re-checks access and needs PRESENCE enabled.
- Nothing is persisted, as the owner decided. Admins get a read-only "Who's here" widget.
**Limitations:**
- EAV fields are read-only for now.
- Multi-select uses JSON arrays without native array operators.
- Other pods pick up definition changes through the overlay's generation refresh.
## Test plan
- [x] `./mvnw -o verify` against a fresh develop SNAPSHOT: 113 tests, checkstyle clean, coverage 96.1% instructions and 99.1% classes.
- [ ] CircleCI on RC.6.
🤖 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
Phases (f) and (g), qbit side, of QRun-IO/qqq#1136 (spec §4 rows 8 and 9, §6.3, §9.2): the custom field designer and presence. Stacked on #8. It builds against the QQQ develop SNAPSHOT and moves to RC.6 together with #8, so it stays a draft until then.
Custom fields:
collaboration_field_definition("Custom Fields", audited) with aclassification(PUBLIC, CONFIDENTIAL, PII or PHI), pluscollaboration_field_value, the EAV fallback.CustomFieldsProvider, aRuntimeMetaDataProviderInterface, contributes each ACTIVE definition as a normal fieldcf_<name>:AiFieldMetaDataclassification;JsonPathStorage("qqq_custom_fields", name)on PostgreSQL and the memory backend, after a one-time check that the column exists;collaborationBuildFieldIndexescreates expression indexes withCREATE INDEX ... CONCURRENTLY, tracking BUILDING, READY or FAILED. WithwithManagedIndexes(false)it only records the DDL.CollaborationSchema.customFieldsDdl(table)prints the one-time column and GIN index.Presence:
PresenceRegistryis in memory and replicated across pods over the core realtime bus, on the internalinstance:presencechannel. Entries expire after 45 s, are capped at 50,000, and one user's tabs are merged.presence.changedis published torecord:andtable:channels.POST,DELETEandGET /qqq/v1/qbit/collaboration/presence. Each re-checks access and needs PRESENCE enabled.Limitations:
Test plan
./mvnw -o verifyagainst a fresh develop SNAPSHOT: 113 tests, checkstyle clean, coverage 96.1% instructions and 99.1% classes.🤖 Generated with Claude Code