You are viewing a plain text version of this content. The canonical link for it is here.
Posted to oak-commits@jackrabbit.apache.org by da...@apache.org on 2026/03/19 21:30:18 UTC
(jackrabbit-oak) branch trunk updated: OAK-12139 : add Claude Code skill for oak-store-document OSGi config (#2801)
This is an automated email from the ASF dual-hosted git repository.
daim pushed a commit to branch trunk
in repository https://gitbox.apache.org/repos/asf/jackrabbit-oak.git
The following commit(s) were added to refs/heads/trunk by this push:
new 8936f9288b OAK-12139 : add Claude Code skill for oak-store-document OSGi config (#2801)
8936f9288b is described below
commit 8936f9288bdc4e86867063c7c44652cd7794a029
Author: Rishabh Kumar <ri...@gmail.com>
AuthorDate: Fri Mar 20 03:00:11 2026 +0530
OAK-12139 : add Claude Code skill for oak-store-document OSGi config (#2801)
* OAK-12139 : add Claude Code skill for oak-store-document OSGi config
* OAK-12139 : move skill to oak-store-document module level
* OAK-12139 : fix RAT exclusion to cover nested .claude directories
* OAK-12139 : split skill into supporting files for readability
* OAK-12139 : fix RAT for submodule-level .claude skills and AGENTS.md
* OAK-12139 : fix skill frontmatter and gitignore trailing newline
* OAK-12139 : update AGENTS.md skill reference to include direct file path
---
.gitignore | 3 +-
.../skills/oak-store-document-add-config/SKILL.md | 137 +++++++++++++++++
.../osgi-config-patterns.md | 167 +++++++++++++++++++++
.../oak-store-document-add-config/test-patterns.md | 157 +++++++++++++++++++
.../wiring-and-verification.md | 75 +++++++++
oak-store-document/AGENTS.md | 10 +-
oak-store-document/pom.xml | 10 ++
7 files changed, 556 insertions(+), 3 deletions(-)
diff --git a/.gitignore b/.gitignore
index c700561aa7..ddce3e839a 100644
--- a/.gitignore
+++ b/.gitignore
@@ -12,4 +12,5 @@ atlassian-ide-plugin.xml
derby.log
.java-version
oak-shaded-guava/dependency-reduced-pom.xml
-.DS_Store
\ No newline at end of file
+.DS_Store
+.claude/settings.local.json
diff --git a/oak-store-document/.claude/skills/oak-store-document-add-config/SKILL.md b/oak-store-document/.claude/skills/oak-store-document-add-config/SKILL.md
new file mode 100644
index 0000000000..de53125a57
--- /dev/null
+++ b/oak-store-document/.claude/skills/oak-store-document-add-config/SKILL.md
@@ -0,0 +1,137 @@
+---
+name: oak-store-document-add-config
+description: Add a new OSGi configuration property to the oak-store-document module. Modifies Configuration.java, DocumentNodeStoreService.java, DocumentNodeStoreBuilder.java, RDBDocumentNodeStoreBuilder.java, Utils.java, and all relevant test files with 90%+ coverage.
+allowed-tools: Read, Edit, Grep, Glob, Bash
+disable-model-invocation: true
+---
+
+# oak-add-config Skill
+
+You are an expert in the Apache Jackrabbit Oak `oak-store-document` module. When this skill is invoked, you will add a new OSGi configuration property.
+
+## Supporting files
+
+- Detailed production code patterns (steps 2–6): [osgi-config-patterns.md](osgi-config-patterns.md)
+- Detailed test patterns (steps 7–10): [test-patterns.md](test-patterns.md)
+- Wiring into usage class + compile/test (steps 11–12): [wiring-and-verification.md](wiring-and-verification.md)
+
+---
+
+## REQUIRED INPUT
+
+The user **must** provide these values when invoking the skill. If any are missing, stop and ask:
+
+| Parameter | Description | Example |
+|-----------|-------------|---------|
+| `configName` | camelCase OSGi attribute method name | `avoidExclusiveMergeLock` |
+| `type` | Java type: `boolean`, `int`, `long`, `String` | `boolean` |
+
+`defaultValue` is **optional** — if not provided, use the Java default for the given type:
+- `boolean` → `false`
+- `int` / `long` → `0`
+- `String` → `""`
+
+The following are **optional** — derive or infer them if not provided:
+
+| Parameter | Description | How to derive if missing |
+|-----------|-------------|--------------------------|
+| `jiraId` | JIRA ticket ID | **Deduce from current branch name** (see below) |
+| `attrName` | Human-readable OSGi display name | Convert `configName` from camelCase to Title Case |
+| `description` | Full OSGi description text | Ask the user for a brief description, then expand it |
+| `scope` | `both`, `mongo-only`, or `rdb-only` | **Ask the user explicitly. Default is `mongo-only`.** |
+| `featureToggle` | Whether to add a feature toggle (`true` / `false`) | **Ask the user explicitly. Default is `false`.** |
+
+> **Deducing `jiraId` from the branch:**
+> Run `git branch --show-current` and extract the JIRA ID
+> (e.g. `OAK-12139` → `jiraId=OAK-12139`; `issue/OAK-12139` → `jiraId=OAK-12139`).
+> If it differs from a user-supplied value, warn. If none can be deduced, ask.
+
+> **Before proceeding, ask both optional questions in a single prompt:**
+> 1. *"Which backends does this config apply to? (mongo-only / rdb-only / both, default: mongo-only)"*
+> 2. *"Do you want a feature toggle for this config? (yes/no, default: no)"*
+
+---
+
+## DERIVED IDENTIFIERS
+
+From the inputs, derive:
+
+- `DEFAULT_CONST` → `DEFAULT_` + UPPER_SNAKE_CASE of `configName`
+ e.g. `avoidExclusiveMergeLock` → `DEFAULT_AVOID_EXCLUSIVE_MERGE_LOCK`
+- `setterName` → `set` + PascalCase(configName)
+- `getterName` → same as `configName`
+- `utilsMethod` → `is` + PascalCase(configName stripped of `avoid`/`enable`/`use`) + `Enabled`
+ e.g. `isAvoidMergeLockEnabled`
+
+**featureToggle=true only:**
+- `FT_NAME_CONST` → `FT_NAME_` + UPPER_SNAKE_CASE of configName
+- `FT_VALUE` → `"FT_"` + UPPER_SNAKE + `"_"` + jiraId
+- `featureField` → `docStore` + PascalCase(configName stripped of trailing `Enabled`) + `Feature`
+
+---
+
+## EXECUTION STEPS
+
+Work through every step in order. Do NOT skip any step.
+
+Read the supporting files as you reach each group of steps.
+
+1. **Read key files** — locate insertion points in all 9 files (listed below)
+2. **Configuration.java** — add `@AttributeDefinition` + import → see [osgi-config-patterns.md](osgi-config-patterns.md)
+3. **DocumentNodeStoreService.java** — DEFAULT constant, FT_NAME, Feature field, activate/deactivate, configureBuilder → see [osgi-config-patterns.md](osgi-config-patterns.md)
+4. **DocumentNodeStoreBuilder.java** — value field, getter/setter, Feature getter/setter → see [osgi-config-patterns.md](osgi-config-patterns.md)
+5. **RDBDocumentNodeStoreBuilder.java** — override to disable (scope=mongo-only) → see [osgi-config-patterns.md](osgi-config-patterns.md)
+6. **Utils.java** — add `isXxxEnabled()` method → see [osgi-config-patterns.md](osgi-config-patterns.md)
+7. **DocumentNodeStoreServiceConfigurationTest.java** → see [test-patterns.md](test-patterns.md)
+8. **MongoDocumentNodeStoreBuilderTest.java** → see [test-patterns.md](test-patterns.md)
+9. **RDBDocumentNodeStoreBuilderTest.java** → see [test-patterns.md](test-patterns.md)
+10. **UtilsTest.java** → see [test-patterns.md](test-patterns.md)
+11. **Wire into usage class** → see [wiring-and-verification.md](wiring-and-verification.md)
+12. **Compile and run tests** → see [wiring-and-verification.md](wiring-and-verification.md)
+
+### Files to read in STEP 1
+
+```
+oak-store-document/src/main/java/org/apache/jackrabbit/oak/plugins/document/Configuration.java
+oak-store-document/src/main/java/org/apache/jackrabbit/oak/plugins/document/DocumentNodeStoreService.java
+oak-store-document/src/main/java/org/apache/jackrabbit/oak/plugins/document/DocumentNodeStoreBuilder.java
+oak-store-document/src/main/java/org/apache/jackrabbit/oak/plugins/document/rdb/RDBDocumentNodeStoreBuilder.java
+oak-store-document/src/main/java/org/apache/jackrabbit/oak/plugins/document/util/Utils.java
+oak-store-document/src/test/java/org/apache/jackrabbit/oak/plugins/document/DocumentNodeStoreServiceConfigurationTest.java
+oak-store-document/src/test/java/org/apache/jackrabbit/oak/plugins/document/mongo/MongoDocumentNodeStoreBuilderTest.java
+oak-store-document/src/test/java/org/apache/jackrabbit/oak/plugins/document/rdb/RDBDocumentNodeStoreBuilderTest.java
+oak-store-document/src/test/java/org/apache/jackrabbit/oak/plugins/document/util/UtilsTest.java
+```
+
+---
+
+## QUALITY CHECKLIST
+
+Before finishing, verify:
+
+- [ ] `Configuration.java` has a new `@AttributeDefinition` entry with correct default import
+- [ ] `DocumentNodeStoreService.java` has DEFAULT constant and configureBuilder value setter
+- [ ] _(featureToggle=true)_ `DocumentNodeStoreService.java` has FT_NAME constant, Feature field, activate registration, deactivate cleanup, and feature setter in configureBuilder
+- [ ] `DocumentNodeStoreBuilder.java` has value field + getter/setter pair
+- [ ] _(featureToggle=true)_ `DocumentNodeStoreBuilder.java` has Feature field + Feature getter/setter pair
+- [ ] `RDBDocumentNodeStoreBuilder.java` overrides value getter/setter to disable (scope=mongo-only)
+- [ ] _(featureToggle=true)_ `RDBDocumentNodeStoreBuilder.java` also overrides Feature getter/setter (scope=mongo-only)
+- [ ] `Utils.java` has `isXxxEnabled()` with proper javadoc
+- [ ] `DocumentNodeStoreServiceConfigurationTest.java` tests default value + override
+- [ ] `MongoDocumentNodeStoreBuilderTest.java` tests default getter (+ null feature if featureToggle=true)
+- [ ] `RDBDocumentNodeStoreBuilderTest.java` has symmetrical default-value tests (scope=both) or override-disabled tests (scope=mongo-only)
+- [ ] `UtilsTest.java` has correct number of tests for featureToggle + scope combination
+- [ ] `DocumentNodeStore.java` reads the config via `{utilsMethod}(builder)` and stores it as a field
+- [ ] Target class receives and uses the field correctly
+- [ ] All tests pass
+- [ ] No new static imports added to any test file
+
+---
+
+## IMPORTANT CONSTRAINTS
+
+- **Assertion style**: match the existing imports in each file — do NOT add new imports. If the file uses `assertFalse(...)` (static), use that; if it uses `Assert.assertFalse(...)`, use that.
+- **No static imports in new production code**
+- **JUnit 4** only (`@Test` from `org.junit.Test`)
+- **Apply standards only to new code** — never reformat or rename existing code
+- Follow the exact naming and placement patterns shown in the supporting files
diff --git a/oak-store-document/.claude/skills/oak-store-document-add-config/osgi-config-patterns.md b/oak-store-document/.claude/skills/oak-store-document-add-config/osgi-config-patterns.md
new file mode 100644
index 0000000000..b8866bdb02
--- /dev/null
+++ b/oak-store-document/.claude/skills/oak-store-document-add-config/osgi-config-patterns.md
@@ -0,0 +1,167 @@
+# Production Code Change Patterns
+
+## STEP 2 — Configuration.java
+
+Add an `@AttributeDefinition` entry at the **end of the interface**, just before the closing `}`.
+
+```java
+@AttributeDefinition(
+ name = "{attrName}",
+ description = "{description}. The Default value is " + {DEFAULT_CONST} +
+ " Note that this value can be overridden via framework property 'oak.documentstore.{configName}'")
+{type} {configName}() default {DEFAULT_CONST};
+```
+
+Also add the import for `DEFAULT_CONST` from `DocumentNodeStoreService` alphabetically:
+```java
+import static org.apache.jackrabbit.oak.plugins.document.DocumentNodeStoreService.{DEFAULT_CONST};
+```
+
+---
+
+## STEP 3 — DocumentNodeStoreService.java
+
+#### 3a. DEFAULT constant
+Find the `static final boolean DEFAULT_*` block (near line 170) and add in alphabetical order:
+```java
+static final {type} {DEFAULT_CONST} = {defaultValue};
+```
+
+#### 3b. FT_NAME constant _(featureToggle=true only)_
+Find the `private static final String FT_NAME_*` block (near line 200):
+```java
+/**
+ * Feature toggle name to {shortened description for javadoc}
+ */
+private static final String {FT_NAME_CONST} = "{FT_VALUE}";
+```
+
+#### 3c. Feature field _(featureToggle=true only)_
+Find the `private Feature *` fields block (near line 263):
+```java
+private Feature {featureField};
+```
+
+#### 3d. Register Feature in `activate()` _(featureToggle=true only)_
+```java
+{featureField} = Feature.newFeature({FT_NAME_CONST}, whiteboard);
+```
+
+#### 3e. Close Feature in `deactivate()` _(featureToggle=true only)_
+Add `{featureField}` to the `closeFeatures(...)` argument list.
+
+#### 3f. Configure builder in `configureBuilder()`
+```java
+.set{PascalCase(configName)}(config.{configName}())
+```
+When `featureToggle=true`, also add immediately after:
+```java
+.setDocStore{PascalCase(featureField minus docStore prefix and Feature suffix)}Feature({featureField})
+```
+
+---
+
+## STEP 4 — DocumentNodeStoreBuilder.java
+
+#### 4a. Feature field _(featureToggle=true only)_
+```java
+private Feature {featureField};
+```
+
+#### 4b. Value field
+```java
+private {type} {getterName};
+```
+
+#### 4c. Getter + Setter for the value
+```java
+public T {setterName}({type} b) {
+ this.{getterName} = b;
+ return thisBuilder();
+}
+
+public {type} {getterName}() {
+ return this.{getterName};
+}
+```
+
+#### 4d. Getter + Setter for the Feature _(featureToggle=true only)_
+```java
+public Feature get{PascalCase(featureField)}() {
+ return {featureField};
+}
+
+public T set{PascalCase(featureField)}(@Nullable Feature {featureField}) {
+ this.{featureField} = {featureField};
+ return thisBuilder();
+}
+```
+
+---
+
+## STEP 5 — RDBDocumentNodeStoreBuilder.java
+
+**scope=mongo-only**: RDB does NOT support the config — override to disable:
+```java
+@Override
+public RDBDocumentNodeStoreBuilder {setterName}({type} b) {
+ return thisBuilder();
+}
+
+@Override
+public {type} {getterName}() {
+ // setting this is not supported for RDB
+ return {disabledValue}; // false for boolean, 0 for int/long, null for String
+}
+```
+
+When `featureToggle=true`, also override Feature getter/setter:
+```java
+@Override
+public RDBDocumentNodeStoreBuilder set{PascalCase(featureField)}(@Nullable Feature f) {
+ return thisBuilder();
+}
+
+@Override
+@Nullable
+public Feature get{PascalCase(featureField)}() {
+ return null;
+}
+```
+
+**scope=rdb-only**: Apply the same override pattern in `MongoDocumentNodeStoreBuilder` instead.
+
+**scope=both**: No overrides needed — base class applies to both.
+
+---
+
+## STEP 6 — Utils.java
+
+Add near the other `isXxxEnabled()` methods (e.g., next to `isThrottlingEnabled`, `isFullGCEnabled`):
+
+**featureToggle=true:**
+```java
+/**
+ * Check whether {attrName} is enabled or not for document store.
+ *
+ * @param builder instance for DocumentNodeStoreBuilder
+ * @return true if {attrName} is enabled else false
+ */
+public static boolean {utilsMethod}(final DocumentNodeStoreBuilder<?> builder) {
+ final Feature {featureField} = builder.get{PascalCase(featureField)}();
+ return builder.{getterName}() || ({featureField} != null && {featureField}.isEnabled());
+}
+```
+
+**featureToggle=false:**
+```java
+/**
+ * Check whether {attrName} is enabled or not for document store.
+ *
+ * @param builder instance for DocumentNodeStoreBuilder
+ * @return true if {attrName} is enabled else false
+ */
+public static boolean {utilsMethod}(final DocumentNodeStoreBuilder<?> builder) {
+ return builder.{getterName}();
+}
+```
diff --git a/oak-store-document/.claude/skills/oak-store-document-add-config/test-patterns.md b/oak-store-document/.claude/skills/oak-store-document-add-config/test-patterns.md
new file mode 100644
index 0000000000..d786fb56cb
--- /dev/null
+++ b/oak-store-document/.claude/skills/oak-store-document-add-config/test-patterns.md
@@ -0,0 +1,157 @@
+# Test Patterns
+
+> **Assertion style rule**: Always match the existing import style in each file — do NOT add new imports to impose a style. See IMPORTANT CONSTRAINTS in SKILL.md.
+
+---
+
+## STEP 7 — DocumentNodeStoreServiceConfigurationTest.java
+
+Uses `Assert.assertEquals(...)` (non-static). Add the import alphabetically:
+```java
+import static org.apache.jackrabbit.oak.plugins.document.DocumentNodeStoreService.{DEFAULT_CONST};
+```
+
+**Test 1**: Add assertion inside the existing `defaultValues()` test:
+```java
+Assert.assertEquals({DEFAULT_CONST}, config.{configName}());
+```
+
+**Test 2**: New test method:
+```java
+@Test
+public void {configName}Enabled() throws Exception {
+ {type} value = {nonDefaultValue};
+ addConfigurationEntry(preset, "{configName}", value);
+ Configuration config = createConfiguration();
+ Assert.assertEquals(value, config.{configName}());
+}
+```
+
+---
+
+## STEP 8 — MongoDocumentNodeStoreBuilderTest.java
+
+Uses static imports (`assertFalse`, `assertNull`). Always add:
+```java
+@Test
+public void {getterName}Disabled() {
+ MongoDocumentNodeStoreBuilder builder = new MongoDocumentNodeStoreBuilder();
+ assertFalse(builder.{getterName}());
+}
+```
+
+When `featureToggle=true`, also add:
+```java
+@Test
+public void {getterName}FeatureToggleEnabled() {
+ MongoDocumentNodeStoreBuilder builder = new MongoDocumentNodeStoreBuilder();
+ assertNull(builder.get{PascalCase(featureField)}());
+}
+```
+
+---
+
+## STEP 9 — RDBDocumentNodeStoreBuilderTest.java
+
+Uses static imports (`assertFalse`, `assertNull`, `mock`, `when`).
+
+**scope=both** — verify base class defaults work for RDB too:
+```java
+@Test
+public void {getterName}Disabled() {
+ RDBDocumentNodeStoreBuilder builder = new RDBDocumentNodeStoreBuilder();
+ assertFalse(builder.{getterName}());
+}
+```
+When `featureToggle=true`, also add:
+```java
+@Test
+public void {getterName}FeatureToggleEnabled() {
+ RDBDocumentNodeStoreBuilder builder = new RDBDocumentNodeStoreBuilder();
+ assertNull(builder.get{PascalCase(featureField)}());
+}
+```
+
+**scope=mongo-only** — verify RDB override ignores the value:
+```java
+@Test
+public void {getterName}Disabled() {
+ RDBDocumentNodeStoreBuilder builder = new RDBDocumentNodeStoreBuilder();
+ builder.{setterName}({nonDefaultValue});
+ assertFalse(builder.{getterName}());
+}
+```
+When `featureToggle=true`, also add:
+```java
+@Test
+public void {getterName}FeatureToggleDisabled() {
+ RDBDocumentNodeStoreBuilder builder = new RDBDocumentNodeStoreBuilder();
+ Feature feature = mock(Feature.class);
+ when(feature.isEnabled()).thenReturn(true);
+ builder.set{PascalCase(featureField)}(feature);
+ assertNull(builder.get{PascalCase(featureField)}());
+}
+```
+
+---
+
+## STEP 10 — UtilsTest.java
+
+Uses static imports for assertions. Check the file's existing Mockito import style and match it.
+
+**Always add** (regardless of `featureToggle`):
+```java
+@Test
+public void {getterName}DefaultValue() {
+ boolean result = Utils.{utilsMethod}(DocumentNodeStoreBuilder.newDocumentNodeStoreBuilder());
+ assertFalse("Feature is disabled by default", result);
+}
+
+@Test
+public void {getterName}EnabledViaConfiguration() {
+ DocumentNodeStoreBuilder<?> builder = DocumentNodeStoreBuilder.newDocumentNodeStoreBuilder();
+ builder.{setterName}(true);
+ assertTrue("Feature is enabled via configuration", Utils.{utilsMethod}(builder));
+}
+```
+
+**featureToggle=true**, also add:
+```java
+@Test
+public void {getterName}ExplicitlyDisabled() {
+ DocumentNodeStoreBuilder<?> builder = DocumentNodeStoreBuilder.newDocumentNodeStoreBuilder();
+ builder.{setterName}(false);
+ Feature feature = Mockito.mock(Feature.class);
+ Mockito.when(feature.isEnabled()).thenReturn(false);
+ builder.set{PascalCase(featureField)}(feature);
+ assertFalse("Feature is disabled explicitly", Utils.{utilsMethod}(builder));
+}
+
+@Test
+public void {getterName}EnabledViaFeatureToggle() {
+ DocumentNodeStoreBuilder<?> builder = DocumentNodeStoreBuilder.newDocumentNodeStoreBuilder();
+ builder.{setterName}(false);
+ Feature feature = Mockito.mock(Feature.class);
+ Mockito.when(feature.isEnabled()).thenReturn(true);
+ builder.set{PascalCase(featureField)}(feature);
+ assertTrue("Feature is enabled via feature toggle", Utils.{utilsMethod}(builder));
+}
+```
+
+**scope=mongo-only**, also add:
+```java
+@Test
+public void {getterName}DisabledForRDB() {
+ DocumentNodeStoreBuilder<?> builder = RDBDocumentNodeStoreBuilder.newRDBDocumentNodeStoreBuilder();
+ builder.{setterName}(true);
+ assertFalse("Feature is disabled for RDB", Utils.{utilsMethod}(builder));
+}
+```
+
+**Test count summary:**
+| featureToggle | scope | UtilsTest count |
+|---|---|---|
+| false | both | 2 |
+| false | mongo-only | 3 |
+| true | both | 4 |
+| true | mongo-only | 5 |
diff --git a/oak-store-document/.claude/skills/oak-store-document-add-config/wiring-and-verification.md b/oak-store-document/.claude/skills/oak-store-document-add-config/wiring-and-verification.md
new file mode 100644
index 0000000000..fd917ed8a0
--- /dev/null
+++ b/oak-store-document/.claude/skills/oak-store-document-add-config/wiring-and-verification.md
@@ -0,0 +1,75 @@
+# Wiring and Verification
+
+## STEP 11 — Wire the config into its usage class
+
+Ask the user:
+> **"Which class do you want to use `{utilsMethod}()` in? (e.g. `DocumentNodeStoreBranch`, `DocumentNodeStore`, etc.)"**
+
+Wait for the answer before proceeding.
+
+### 11a. `DocumentNodeStore.java` — read config from builder, store as field
+
+Read `DocumentNodeStore.java` first to understand existing field and constructor patterns.
+
+Add the field declaration near similar fields:
+```java
+private final boolean {fieldName};
+```
+
+In the constructor `DocumentNodeStore(DocumentNodeStoreBuilder<?> builder)`, alongside similar assignments:
+```java
+this.{fieldName} = {utilsMethod}(builder);
+```
+
+Add the static import alongside other `Utils.*` imports at the top:
+```java
+import static ...util.Utils.{utilsMethod};
+```
+
+### 11b. Target class — pass the value through
+
+If the target class is NOT `DocumentNodeStore` itself (e.g. `DocumentNodeStoreBranch`):
+
+1. Find where `DocumentNodeStore` instantiates the target class and pass the field as an extra argument.
+2. In the target class constructor, accept and assign it:
+ ```java
+ private final boolean {fieldName};
+ // in constructor:
+ this.{fieldName} = {fieldName};
+ ```
+3. Use `this.{fieldName}` wherever the logic needs it.
+
+If the target IS `DocumentNodeStore`, skip 11b — the field is already there from 11a.
+
+### 11c. Update ALL existing constructor call sites (STRICTLY ENFORCED)
+
+When adding a new parameter to a constructor:
+- **NEVER add a backward-compatibility overload**
+- Grep all call sites (prod + test) and update every one to pass the default value
+
+```bash
+grep -rn "new TargetClass(" oak-store-document/src/
+```
+
+For each call site, add the default value (e.g. `false` for boolean, `0` for int/long, `null` for String).
+
+---
+
+## STEP 12 — Compile and run tests
+
+```bash
+cd "$(git rev-parse --show-toplevel)"
+mvn compile -pl oak-store-document -q 2>&1 | tail -20
+```
+
+If compilation fails, diagnose and fix, then retry.
+
+```bash
+mvn test -pl oak-store-document -am \
+ -Dtest="DocumentNodeStoreServiceConfigurationTest,MongoDocumentNodeStoreBuilderTest,RDBDocumentNodeStoreBuilderTest,UtilsTest" \
+ 2>&1 | tail -40
+```
+
+Fix any failures and re-run until all pass.
+
+> **Note**: Pre-existing unrelated compile errors (e.g. `NodeCache.java`) should be ignored — verify they exist on trunk before treating them as caused by your changes.
diff --git a/oak-store-document/AGENTS.md b/oak-store-document/AGENTS.md
index 856f47d1c4..3dc9d7faec 100644
--- a/oak-store-document/AGENTS.md
+++ b/oak-store-document/AGENTS.md
@@ -125,7 +125,10 @@ DocumentNodeStore ← Runtime instance
OSGi PID: `org.apache.jackrabbit.oak.plugins.document.DocumentNodeStoreService`
When adding a new OSGi config property, all four files above must be updated plus tests.
-Use the `/oak-store-document-add-config` skill to automate this workflow.
+Follow the workflow in
+`oak-store-document/.claude/skills/oak-store-document-add-config/SKILL.md`
+and the supporting files in the same directory. In Claude, this workflow is exposed as
+the `/oak-store-document-add-config` skill.
## Background Operations
@@ -432,4 +435,7 @@ mvn verify -pl oak-store-document -PintegrationTesting
never assume the local clock matches the backend clock
- **Adding OSGi config** — changes to `Configuration.java` require matching updates in
`DocumentNodeStoreService`, `DocumentNodeStoreBuilder`, `RDBDocumentNodeStoreBuilder`,
- and all relevant test files; use the `/oak-store-document-add-config` skill
+ and all relevant test files; follow
+ `oak-store-document/.claude/skills/oak-store-document-add-config/SKILL.md`
+ and its supporting files. In Claude, this workflow is exposed as the
+ `/oak-store-document-add-config` skill
diff --git a/oak-store-document/pom.xml b/oak-store-document/pom.xml
index e84ddbe204..49ce04e3a9 100644
--- a/oak-store-document/pom.xml
+++ b/oak-store-document/pom.xml
@@ -56,6 +56,16 @@
</instructions>
</configuration>
</plugin>
+ <plugin>
+ <groupId>org.apache.rat</groupId>
+ <artifactId>apache-rat-plugin</artifactId>
+ <configuration>
+ <excludes>
+ <exclude>.claude/**</exclude>
+ <exclude>AGENTS.md</exclude>
+ </excludes>
+ </configuration>
+ </plugin>
<plugin>
<artifactId>maven-failsafe-plugin</artifactId>
<configuration>