From 5caa78a190de0400a1979b908b5a3dc11589209f Mon Sep 17 00:00:00 2001 From: James Maes Date: Thu, 10 Sep 2026 07:09:37 -0500 Subject: [PATCH 1/4] fix(template): complete QQQ 4.0 application migration --- .circleci/config.yml | 2 +- README.md | 23 +++---- docs/00-getting-started.md | 57 ++++++---------- docs/01-tables-and-relationships.md | 3 +- docs/02-processes.md | 57 +--------------- docs/03-widgets-and-ui.md | 68 ++++--------------- pom.xml | 2 +- .../qbits/example/ExampleAppQBitProducer.java | 20 ++++-- .../ExampleProcessMetaDataProducer.java | 12 ++-- .../example/processes/ExampleProcessStep.java | 18 ++++- ...xampleDashboardWidgetMetaDataProducer.java | 5 +- .../ExampleDashboardWidgetRenderer.java | 20 +++--- 12 files changed, 96 insertions(+), 191 deletions(-) diff --git a/.circleci/config.yml b/.circleci/config.yml index 8828026..f770741 100644 --- a/.circleci/config.yml +++ b/.circleci/config.yml @@ -6,4 +6,4 @@ orbs: workflows: build: jobs: - - qqq-orb/mvn_test + - qqq-orb/mvn_test_only diff --git a/README.md b/README.md index f36871f..f225c00 100644 --- a/README.md +++ b/README.md @@ -12,30 +12,23 @@ Use this template when building: ## Quick Start -1. Click "Use this template" on GitHub -2. Clone your new repository -3. Run the customization script: - ```bash - python scripts/customize_template.py --name my-app --package com.mycompany.qbits.myapp - ``` -4. Implement your tables, processes, and widgets -5. Add your QAppSection for UI navigation +Requires **Java 21**, **Maven 3.8+**, and QQQ **4.0.0**. Create a repository with **Use this template**, customize its Maven coordinates and Java package/classes, then run `mvn clean verify`. Follow the [Getting Started guide](docs/00-getting-started.md) for the full sequence. ## Structure ``` src/main/java/com/kingsrook/qbits/example/ -├── ExampleAppQBitConfig.java # Configuration options -├── ExampleAppQBitProducer.java # Entry point, registers QAppSection +├── ExampleAppQBitConfig.java +├── ExampleAppQBitProducer.java ├── model/ │ ├── ExampleEntity.java │ └── ExampleChildEntity.java ├── processes/ -│ └── ExampleProcess.java -├── widgets/ -│ └── ExampleDashboardWidget.java -└── api/ - └── ExampleApiHandler.java +│ ├── ExampleProcessStep.java +│ └── ExampleProcessMetaDataProducer.java +└── widgets/ + ├── ExampleDashboardWidgetRenderer.java + └── ExampleDashboardWidgetMetaDataProducer.java ``` ## Key Characteristics diff --git a/docs/00-getting-started.md b/docs/00-getting-started.md index d869daf..02d1a9d 100644 --- a/docs/00-getting-started.md +++ b/docs/00-getting-started.md @@ -1,47 +1,30 @@ # Getting Started -This template helps you create Application QBits - complete mini-applications with UI navigation. +Requires Java 21, Maven 3.8 or later, and a host application using QQQ 4.0.0. Dependencies resolve from Maven Central. -## Quick Start +1. Choose **Use this template** on GitHub, then clone the repository you created. +2. In `pom.xml`, set your own `groupId`, `artifactId`, name, description, and version. +3. Use your IDE's package refactoring to rename `com.kingsrook.qbits.example` throughout `src/`. Rename the `Example*` classes and update their imports and references with the IDE's rename refactoring. +4. Update the QBit producer's `GROUP_ID`, `ARTIFACT_ID`, and `VERSION` constants to match your project. Give the metadata names their own stable names before combining the QBit with other examples. +5. Build the generated project: -1. **Create from template:** - ```bash - gh repo create my-company/qbit-my-app --template QRun-IO/qbit-template-application - ``` +```bash +mvn clean verify +``` -2. **Run customization script:** - ```bash - python scripts/customize_template.py --name my-app --package com.mycompany.qbits.myapp - ``` +The repository provides example Java sources; customization uses normal package/class refactoring. Keep example coordinates unpublished. After customizing, add tests for your QBit's behavior before setting up its publishing workflow. -3. **Build and test:** - ```bash - mvn clean test - ``` +## Register with a host application -## Key Differences from Other QBit Types - -| Feature | Extension | Data | Application | -|---------|-----------|------|-------------| -| QAppSection | No | Optional | **Required** | -| Tables | 0-few | Few (reference) | Many (business) | -| Processes | Customizers | Sync | Full business logic | -| Widgets | No | No | **Yes** | - -## What to Customize - -1. `ExampleAppQBitConfig.java` - Add your configuration options -2. `ExampleAppQBitProducer.java` - Update QAppSection with your tables -3. `model/` - Replace with your entities -4. `processes/` - Implement your business logic -5. `widgets/` - Create your dashboard components - -## Usage in Host Application +The following uses the original example names; substitute the names chosen above: ```java -new MyAppQBitProducer() - .withConfig(new MyAppQBitConfig() - .withBackendName("rdbms") - .withTableNamePrefix("myapp")) - .produce(qInstance, "my-app"); +new ExampleAppQBitProducer() + .withConfig(new ExampleAppQBitConfig() + .withBackendName("rdbms")) + .produce(qInstance, "my-application"); ``` + +Register the configured backend in the host first. If you add table prefixes or multiple QBit instances, update related table references, possible-value-source names, and process/widget names consistently. A table prefix alone does not namespace all metadata. + +[Tables and Relationships](01-tables-and-relationships.md) describes the model; the `processes/` and `widgets/` directories contain the remaining examples. diff --git a/docs/01-tables-and-relationships.md b/docs/01-tables-and-relationships.md index de78bee..c661ae3 100644 --- a/docs/01-tables-and-relationships.md +++ b/docs/01-tables-and-relationships.md @@ -42,8 +42,9 @@ Application QBits support optional table prefixing: new MyAppQBitProducer() .withConfig(new MyAppQBitConfig() + .withBackendName("rdbms") .withTableNamePrefix("sales")) .produce(qInstance, "sales-app"); ``` -The producer automatically applies the prefix to all table names. +The producer applies the prefix to table names. When extending this pattern, also update possible-value-source names, relationship targets, process tables, and navigation references; table renaming alone does not provide complete multi-instance support. diff --git a/docs/02-processes.md b/docs/02-processes.md index 0407eee..ba48fa7 100644 --- a/docs/02-processes.md +++ b/docs/02-processes.md @@ -1,58 +1,7 @@ # Processes -Application QBits include processes for business logic execution. +Start with [ExampleProcessMetaDataProducer](../src/main/java/com/kingsrook/qbits/example/processes/ExampleProcessMetaDataProducer.java) and [ExampleProcessStep](../src/main/java/com/kingsrook/qbits/example/processes/ExampleProcessStep.java). The producer defines the process, its table, inputs, and backend step; the step transforms `PENDING` records to `PROCESSED` and returns them in the output. -## Process Structure +In QQQ 4.0, import `MetaDataProducerInterface` from `com.kingsrook.qqq.backend.core.model.metadata`. Define process inputs through `withInputMetaData(new QFunctionInputMetaData().withField(...))`. An `AbstractTransformStep` must implement both `runOnePage(RunBackendStepInput, RunBackendStepOutput)` and `getProcessSummary(RunBackendStepOutput, boolean)`; keep the example's implementations when renaming it. -```java -public class MyProcessMetaDataProducer implements MetaDataProducerInterface -{ - public static final String NAME = "myProcess"; - - @Override - public QProcessMetaData produce(QInstance qInstance) - { - return new QProcessMetaData() - .withName(NAME) - .withLabel("My Process") - .withTableName(MyEntity.TABLE_NAME) - .withStepList(List.of( - new QBackendStepMetaData() - .withName("execute") - .withCode(new QCodeReference(MyProcessStep.class)) - )); - } -} -``` - -## Process Steps - -Implement `AbstractTransformStep` for record processing: - -```java -public class MyProcessStep extends AbstractTransformStep -{ - @Override - public void runOnePage(RunBackendStepInput input, RunBackendStepOutput output) - { - for(QRecord record : input.getRecords()) - { - // Business logic here - output.addRecord(record); - } - } -} -``` - -## Including in QAppSection - -Add processes to your app's navigation: - -```java -return new QAppSection() - .withName("myApp") - .withTables(List.of(...)) - .withProcesses(List.of( - config.applyPrefix(MyProcess.NAME) - )); -``` +To make the process discoverable in navigation, add its registered name to a `QAppSection` through `withProcesses(List.of(ExampleProcessMetaDataProducer.NAME))`, then include the section in a `QAppMetaData` registered with `qInstance.addApp(...)`. Use the actual registered name if you introduce a prefix. Add tests for record changes and failure cases when replacing the example business logic. diff --git a/docs/03-widgets-and-ui.md b/docs/03-widgets-and-ui.md index d16fff5..8dd11f8 100644 --- a/docs/03-widgets-and-ui.md +++ b/docs/03-widgets-and-ui.md @@ -1,61 +1,23 @@ # Widgets and UI -Application QBits include dashboard widgets and UI navigation via QAppSection. - -## QAppSection (Required) - -Every Application QBit must register a QAppSection: +The application producer registers a `QAppMetaData` containing a `QAppSection`. The section references metadata names already registered in the instance; it is not registered directly on `QInstance`. ```java -private QAppSection produceAppSection(MyAppConfig config, String qbitName) -{ - return new QAppSection() - .withName(config.applyPrefix("myApp")) - .withLabel("My Application") - .withIcon(new QIcon().withName("dashboard")) - .withTables(List.of( - config.applyPrefix(Order.TABLE_NAME), - config.applyPrefix(OrderLine.TABLE_NAME) - )) - .withProcesses(List.of( - config.applyPrefix(ProcessOrder.NAME) - )) - .withWidgets(List.of( - config.applyPrefix(OrderDashboard.NAME) - )) - .withSourceQBitName(qbitName); -} +QAppSection section = new QAppSection() + .withName("exampleSection") + .withLabel("Example Application") + .withTables(List.of(ExampleEntity.TABLE_NAME, ExampleChildEntity.TABLE_NAME)) + .withProcesses(List.of(ExampleProcessMetaDataProducer.NAME)) + .withWidgets(List.of(ExampleDashboardWidgetMetaDataProducer.NAME)); + +qInstance.addApp(new QAppMetaData() + .withName("exampleApp") + .withLabel("Example Application") + .withSection(section)); ``` -## Dashboard Widgets +Use [ExampleAppQBitProducer](../src/main/java/com/kingsrook/qbits/example/ExampleAppQBitProducer.java) as the starting point. Keep these references consistent when enabling/disabling tables or introducing prefixes. -Create widgets with metadata producer and renderer: +The widget has two parts: [its metadata producer](../src/main/java/com/kingsrook/qbits/example/widgets/ExampleDashboardWidgetMetaDataProducer.java) declares a statistics widget and its renderer, and [its renderer](../src/main/java/com/kingsrook/qbits/example/widgets/ExampleDashboardWidgetRenderer.java) returns a `RenderWidgetOutput` containing `StatisticsData`. Replace the example count with a query or calculation. Choose a widget type that matches the returned data model. -```java -// MetaData Producer -public class MyWidgetMetaDataProducer implements MetaDataProducerInterface -{ - @Override - public QWidgetMetaData produce(QInstance qInstance) - { - return new QWidgetMetaData() - .withName("myWidget") - .withLabel("My Dashboard") - .withType("chart") - .withCodeReference(new QCodeReference(MyWidgetRenderer.class)); - } -} - -// Renderer -public class MyWidgetRenderer extends AbstractWidgetRenderer -{ - @Override - public RenderWidgetOutput render(RenderWidgetInput input) - { - Map data = new HashMap<>(); - data.put("title", "Dashboard"); - // Add chart data, counts, etc. - return new RenderWidgetOutput(data); - } -} -``` +For QQQ 4.0, `AbstractWidgetRenderer` is in `com.kingsrook.qqq.backend.core.actions.dashboard.widgets`; `RenderWidgetInput` and `RenderWidgetOutput` are in `com.kingsrook.qqq.backend.core.model.actions.widgets`. diff --git a/pom.xml b/pom.xml index 07f81b5..0ce6b8e 100644 --- a/pom.xml +++ b/pom.xml @@ -16,7 +16,7 @@ 21 21 UTF-8 - 0.35.0 + 4.0.0 diff --git a/src/main/java/com/kingsrook/qbits/example/ExampleAppQBitProducer.java b/src/main/java/com/kingsrook/qbits/example/ExampleAppQBitProducer.java index 9dac58d..e5fb536 100644 --- a/src/main/java/com/kingsrook/qbits/example/ExampleAppQBitProducer.java +++ b/src/main/java/com/kingsrook/qbits/example/ExampleAppQBitProducer.java @@ -17,6 +17,7 @@ import com.kingsrook.qqq.backend.core.model.metadata.MetaDataProducerHelper; import com.kingsrook.qqq.backend.core.model.metadata.MetaDataProducerInterface; import com.kingsrook.qqq.backend.core.model.metadata.MetaDataProducerOutput; import com.kingsrook.qqq.backend.core.model.metadata.QInstance; +import com.kingsrook.qqq.backend.core.model.metadata.layout.QAppMetaData; import com.kingsrook.qqq.backend.core.model.metadata.layout.QAppSection; import com.kingsrook.qqq.backend.core.model.metadata.layout.QIcon; import com.kingsrook.qqq.backend.core.model.metadata.qbits.QBitMetaData; @@ -104,20 +105,20 @@ public class ExampleAppQBitProducer implements QBitProducer } ///////////////////////////////////////////////////////////////////////// - // Register QAppSection for UI navigation (REQUIRED for app QBits) // + // Register QAppMetaData for UI navigation (REQUIRED for app QBits) // ///////////////////////////////////////////////////////////////////////// - qInstance.addAppSection(produceAppSection(config, qBitMetaData.getName())); + qInstance.addApp(produceApp(config, qBitMetaData.getName())); } /******************************************************************************* - ** Produce the QAppSection for UI navigation. + ** Produce the QAppMetaData for UI navigation. ** ** This is REQUIRED for Application QBits - it defines how users navigate ** to the tables, processes, and widgets in the UI. *******************************************************************************/ - private QAppSection produceAppSection(ExampleAppQBitConfig config, String qbitName) + private QAppMetaData produceApp(ExampleAppQBitConfig config, String qbitName) { List tables = new ArrayList<>(); tables.add(config.applyPrefix(ExampleEntity.TABLE_NAME)); @@ -127,12 +128,17 @@ public class ExampleAppQBitProducer implements QBitProducer tables.add(config.applyPrefix(ExampleChildEntity.TABLE_NAME)); } - return new QAppSection() + QAppSection section = new QAppSection() + .withName(config.applyPrefix("exampleSection")) + .withLabel("Example Application") + .withIcon(new QIcon().withName("dashboard")) + .withTables(tables); + + return new QAppMetaData() .withName(config.applyPrefix("exampleApp")) .withLabel("Example Application") .withIcon(new QIcon().withName("dashboard")) - .withTables(tables) - .withSourceQBitName(qbitName); + .withSection(section); } diff --git a/src/main/java/com/kingsrook/qbits/example/processes/ExampleProcessMetaDataProducer.java b/src/main/java/com/kingsrook/qbits/example/processes/ExampleProcessMetaDataProducer.java index ba49c74..93cfee1 100644 --- a/src/main/java/com/kingsrook/qbits/example/processes/ExampleProcessMetaDataProducer.java +++ b/src/main/java/com/kingsrook/qbits/example/processes/ExampleProcessMetaDataProducer.java @@ -7,12 +7,13 @@ package com.kingsrook.qbits.example.processes; import java.util.List; import com.kingsrook.qqq.backend.core.exceptions.QException; import com.kingsrook.qqq.backend.core.model.metadata.QInstance; +import com.kingsrook.qqq.backend.core.model.metadata.MetaDataProducerInterface; import com.kingsrook.qqq.backend.core.model.metadata.code.QCodeReference; import com.kingsrook.qqq.backend.core.model.metadata.fields.QFieldMetaData; import com.kingsrook.qqq.backend.core.model.metadata.fields.QFieldType; import com.kingsrook.qqq.backend.core.model.metadata.processes.QBackendStepMetaData; +import com.kingsrook.qqq.backend.core.model.metadata.processes.QFunctionInputMetaData; import com.kingsrook.qqq.backend.core.model.metadata.processes.QProcessMetaData; -import com.kingsrook.qqq.backend.core.model.MetaDataProducerInterface; import com.kingsrook.qbits.example.model.ExampleEntity; @@ -32,15 +33,14 @@ public class ExampleProcessMetaDataProducer implements MetaDataProducerInterface .withName(NAME) .withLabel("Example Process") .withTableName(ExampleEntity.TABLE_NAME) - .withInputFields(List.of( - new QFieldMetaData("confirmAction", QFieldType.BOOLEAN) - .withLabel("Confirm Action") - .withDefaultValue(false) - )) .withStepList(List.of( new QBackendStepMetaData() .withName("process") .withCode(new QCodeReference(ExampleProcessStep.class)) + .withInputData(new QFunctionInputMetaData() + .withField(new QFieldMetaData("confirmAction", QFieldType.BOOLEAN) + .withLabel("Confirm Action") + .withDefaultValue(false))) )); } } diff --git a/src/main/java/com/kingsrook/qbits/example/processes/ExampleProcessStep.java b/src/main/java/com/kingsrook/qbits/example/processes/ExampleProcessStep.java index 14fcac0..216d83d 100644 --- a/src/main/java/com/kingsrook/qbits/example/processes/ExampleProcessStep.java +++ b/src/main/java/com/kingsrook/qbits/example/processes/ExampleProcessStep.java @@ -9,11 +9,13 @@ package com.kingsrook.qbits.example.processes; +import java.util.ArrayList; import java.util.List; -import com.kingsrook.qqq.backend.core.actions.processes.RunBackendStepInput; -import com.kingsrook.qqq.backend.core.actions.processes.RunBackendStepOutput; import com.kingsrook.qqq.backend.core.exceptions.QException; import com.kingsrook.qqq.backend.core.logging.QLogger; +import com.kingsrook.qqq.backend.core.model.actions.processes.ProcessSummaryLineInterface; +import com.kingsrook.qqq.backend.core.model.actions.processes.RunBackendStepInput; +import com.kingsrook.qqq.backend.core.model.actions.processes.RunBackendStepOutput; import com.kingsrook.qqq.backend.core.model.data.QRecord; import com.kingsrook.qqq.backend.core.processes.implementations.etl.streamedwithfrontend.AbstractTransformStep; import static com.kingsrook.qqq.backend.core.logging.LogUtils.logPair; @@ -50,4 +52,16 @@ public class ExampleProcessStep extends AbstractTransformStep LOG.info("Processing complete", logPair("processed", records.size())); } + + + + /******************************************************************************* + ** Return the process summary. Override this method in subclasses to provide + ** meaningful summary lines for the result screen. + *******************************************************************************/ + @Override + public ArrayList getProcessSummary(RunBackendStepOutput runBackendStepOutput, boolean isForResultScreen) + { + return (new ArrayList<>()); + } } diff --git a/src/main/java/com/kingsrook/qbits/example/widgets/ExampleDashboardWidgetMetaDataProducer.java b/src/main/java/com/kingsrook/qbits/example/widgets/ExampleDashboardWidgetMetaDataProducer.java index 6ce47f3..a2bec6c 100644 --- a/src/main/java/com/kingsrook/qbits/example/widgets/ExampleDashboardWidgetMetaDataProducer.java +++ b/src/main/java/com/kingsrook/qbits/example/widgets/ExampleDashboardWidgetMetaDataProducer.java @@ -7,10 +7,11 @@ package com.kingsrook.qbits.example.widgets; import com.kingsrook.qqq.backend.core.exceptions.QException; +import com.kingsrook.qqq.backend.core.model.dashboard.widgets.WidgetType; import com.kingsrook.qqq.backend.core.model.metadata.QInstance; import com.kingsrook.qqq.backend.core.model.metadata.code.QCodeReference; import com.kingsrook.qqq.backend.core.model.metadata.dashboard.QWidgetMetaData; -import com.kingsrook.qqq.backend.core.model.MetaDataProducerInterface; +import com.kingsrook.qqq.backend.core.model.metadata.MetaDataProducerInterface; public class ExampleDashboardWidgetMetaDataProducer implements MetaDataProducerInterface @@ -28,7 +29,7 @@ public class ExampleDashboardWidgetMetaDataProducer implements MetaDataProducerI return new QWidgetMetaData() .withName(NAME) .withLabel("Example Dashboard") - .withType("chart") + .withType(WidgetType.STATISTICS.getType()) .withCodeReference(new QCodeReference(ExampleDashboardWidgetRenderer.class)); } } diff --git a/src/main/java/com/kingsrook/qbits/example/widgets/ExampleDashboardWidgetRenderer.java b/src/main/java/com/kingsrook/qbits/example/widgets/ExampleDashboardWidgetRenderer.java index 42e487e..6bec978 100644 --- a/src/main/java/com/kingsrook/qbits/example/widgets/ExampleDashboardWidgetRenderer.java +++ b/src/main/java/com/kingsrook/qbits/example/widgets/ExampleDashboardWidgetRenderer.java @@ -6,12 +6,11 @@ package com.kingsrook.qbits.example.widgets; -import java.util.HashMap; -import java.util.Map; -import com.kingsrook.qqq.backend.core.actions.dashboard.AbstractWidgetRenderer; -import com.kingsrook.qqq.backend.core.actions.dashboard.RenderWidgetInput; -import com.kingsrook.qqq.backend.core.actions.dashboard.RenderWidgetOutput; +import com.kingsrook.qqq.backend.core.actions.dashboard.widgets.AbstractWidgetRenderer; import com.kingsrook.qqq.backend.core.exceptions.QException; +import com.kingsrook.qqq.backend.core.model.actions.widgets.RenderWidgetInput; +import com.kingsrook.qqq.backend.core.model.actions.widgets.RenderWidgetOutput; +import com.kingsrook.qqq.backend.core.model.dashboard.widgets.StatisticsData; public class ExampleDashboardWidgetRenderer extends AbstractWidgetRenderer @@ -22,16 +21,13 @@ public class ExampleDashboardWidgetRenderer extends AbstractWidgetRenderer @Override public RenderWidgetOutput render(RenderWidgetInput input) throws QException { - Map data = new HashMap<>(); - data.put("title", "Example Dashboard"); - data.put("description", "This is an example widget for the Application QBit template."); - ///////////////////////////////////////////////////////////////////////// // Add chart data, counts, or other dashboard information here // ///////////////////////////////////////////////////////////////////////// - data.put("totalCount", 0); - data.put("activeCount", 0); + StatisticsData data = new StatisticsData() + .withCount(0) + .withCountContext("Total items"); - return new RenderWidgetOutput(data); + return (new RenderWidgetOutput(data)); } } -- 2.54.0 From 45eb00811753a74f99382c2cca2d2a4fd69f7489 Mon Sep 17 00:00:00 2001 From: James Maes Date: Thu, 10 Sep 2026 07:54:13 -0500 Subject: [PATCH 2/4] fix(ci): avoid interactive package restart prompts --- .circleci/config.yml | 14 +++++++++++++- 1 file changed, 13 insertions(+), 1 deletion(-) diff --git a/.circleci/config.yml b/.circleci/config.yml index f770741..010cd62 100644 --- a/.circleci/config.yml +++ b/.circleci/config.yml @@ -3,7 +3,19 @@ version: 2.1 orbs: qqq-orb: kingsrook/qqq-orb@0.5.0 +commands: + configure-package-install: + steps: + - run: + name: Keep package installation unattended + command: | + # These build machines are discarded after the job. + sudo install -d /etc/needrestart/conf.d + printf '%s\n' '$nrconf{restart} = "l";' | sudo tee /etc/needrestart/conf.d/99-qqq-ci.conf >/dev/null + workflows: build: jobs: - - qqq-orb/mvn_test_only + - qqq-orb/mvn_test_only: + pre-steps: + - configure-package-install -- 2.54.0 From 1a14cd26d16d1b900c33ef420dfcab46ba4fafa8 Mon Sep 17 00:00:00 2001 From: James Maes Date: Thu, 10 Sep 2026 08:37:06 -0500 Subject: [PATCH 3/4] docs: align guides and examples with QQQ 4.0 --- CLAUDE.md | 4 ++++ docs/00-getting-started.md | 2 +- 2 files changed, 5 insertions(+), 1 deletion(-) diff --git a/CLAUDE.md b/CLAUDE.md index 334f05d..2a6b871 100644 --- a/CLAUDE.md +++ b/CLAUDE.md @@ -3,6 +3,10 @@ GitHub template repository for scaffolding Application QBits (complete mini-applications with tables, processes, widgets, and UI navigation) on the QQQ platform. +## Current 4.0 preparation — 2026-09-10 + +The historical review below predates the 4.0 migration. This release branch targets Java 21 and QQQ 4.0.0; source and renamed/generated scaffolds pass clean verification against the local RC.3 candidate. Final Maven Central publication is still pending. The corrected APIs, license references and actual template commands are in README.md and docs/00-getting-started.md. Preserve the documented multi-instance and scaffold limitations. + ## Knowledge base Reviewed dossier and platform knowledge live in the second-brain vault: diff --git a/docs/00-getting-started.md b/docs/00-getting-started.md index 02d1a9d..dab3c4a 100644 --- a/docs/00-getting-started.md +++ b/docs/00-getting-started.md @@ -1,6 +1,6 @@ # Getting Started -Requires Java 21, Maven 3.8 or later, and a host application using QQQ 4.0.0. Dependencies resolve from Maven Central. +Targets Java 21, Maven 3.8 or later, and QQQ 4.0.0. Final 4.0.0 artifacts are not published yet; these instructions become directly usable after publication. For candidate validation, build the selected candidate and supply `-Dqqq.version=` rather than installing an RC under the final version. 1. Choose **Use this template** on GitHub, then clone the repository you created. 2. In `pom.xml`, set your own `groupId`, `artifactId`, name, description, and version. -- 2.54.0 From d43d6482bcb455ffbd588e12cbfeaf7888fac6c7 Mon Sep 17 00:00:00 2001 From: James Maes Date: Thu, 24 Sep 2026 13:29:17 -0500 Subject: [PATCH 4/4] docs(release): finalize QQQ 4.0 template instructions Refs QRun-IO/qqq#631. --- CLAUDE.md | 4 ++-- docs/00-getting-started.md | 2 +- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/CLAUDE.md b/CLAUDE.md index 2a6b871..7e48329 100644 --- a/CLAUDE.md +++ b/CLAUDE.md @@ -3,9 +3,9 @@ GitHub template repository for scaffolding Application QBits (complete mini-applications with tables, processes, widgets, and UI navigation) on the QQQ platform. -## Current 4.0 preparation — 2026-09-10 +## QQQ 4.0 baseline — 2026-09-24 -The historical review below predates the 4.0 migration. This release branch targets Java 21 and QQQ 4.0.0; source and renamed/generated scaffolds pass clean verification against the local RC.3 candidate. Final Maven Central publication is still pending. The corrected APIs, license references and actual template commands are in README.md and docs/00-getting-started.md. Preserve the documented multi-instance and scaffold limitations. +The historical review below predates the 4.0 migration. This template targets Java 21 and the public QQQ 4.0.0 release from Maven Central. The corrected APIs, license references and actual template commands are in README.md and docs/00-getting-started.md. Preserve the documented multi-instance and scaffold limitations. ## Knowledge base diff --git a/docs/00-getting-started.md b/docs/00-getting-started.md index dab3c4a..f235d60 100644 --- a/docs/00-getting-started.md +++ b/docs/00-getting-started.md @@ -1,6 +1,6 @@ # Getting Started -Targets Java 21, Maven 3.8 or later, and QQQ 4.0.0. Final 4.0.0 artifacts are not published yet; these instructions become directly usable after publication. For candidate validation, build the selected candidate and supply `-Dqqq.version=` rather than installing an RC under the final version. +Targets Java 21, Maven 3.8 or later, and QQQ 4.0.0. Maven resolves the framework dependencies from Maven Central. 1. Choose **Use this template** on GitHub, then clone the repository you created. 2. In `pom.xml`, set your own `groupId`, `artifactId`, name, description, and version. -- 2.54.0