diff --git a/buildSrc/build.gradle b/buildSrc/build.gradle
index fdd38446fc..ec117017ac 100644
--- a/buildSrc/build.gradle
+++ b/buildSrc/build.gradle
@@ -43,5 +43,9 @@ gradlePlugin {
id = "linea.solc-toolchain"
implementationClass = "SolcToolchainPlugin"
}
+ create("ConfigDocsPlugin") {
+ id = "linea.config-docs"
+ implementationClass = "ConfigDocsPlugin"
+ }
}
}
diff --git a/buildSrc/src/main/groovy/ConfigDocsExtension.groovy b/buildSrc/src/main/groovy/ConfigDocsExtension.groovy
new file mode 100644
index 0000000000..3375d39e5a
--- /dev/null
+++ b/buildSrc/src/main/groovy/ConfigDocsExtension.groovy
@@ -0,0 +1,10 @@
+/**
+ * Configuration for the {@code linea.config-docs} convention plugin.
+ */
+class ConfigDocsExtension {
+ /**
+ * Fully-qualified name of the app's {@code linea.config.docs.ConfigDocsSpec} implementation
+ * (a Kotlin object), e.g. {@code "linea.coordinator.config.v2.docs.CoordinatorConfigDocsSpec"}.
+ */
+ String spec
+}
\ No newline at end of file
diff --git a/buildSrc/src/main/groovy/ConfigDocsPlugin.groovy b/buildSrc/src/main/groovy/ConfigDocsPlugin.groovy
new file mode 100644
index 0000000000..c790dbeb6e
--- /dev/null
+++ b/buildSrc/src/main/groovy/ConfigDocsPlugin.groovy
@@ -0,0 +1,62 @@
+import org.gradle.api.Plugin
+import org.gradle.api.Project
+import org.gradle.api.tasks.JavaExec
+import org.gradle.api.tasks.SourceSetContainer
+
+/**
+ * Wires the config-documentation check/generate tasks for an application module.
+ *
+ * Creates a dedicated {@code configDocs} source set (compiled against {@code main} plus the
+ * {@code jvm-libs:linea:config-docs} engine, and kept out of the production jar), and registers
+ * {@code checkConfigDocs} and {@code generateConfigDocs} tasks that run the engine's generic entry
+ * points against the app's {@code ConfigDocsSpec}.
+ *
+ * Apply after the Kotlin/application conventions plugin and configure the spec:
+ *
+ * plugins { id 'net.consensys.zkevm.kotlin-application-conventions'; id 'linea.config-docs' }
+ * configDocs { spec = "com.example.MyConfigDocsSpec" }
+ *
+ */
+class ConfigDocsPlugin implements Plugin {
+ @Override
+ void apply(Project project) {
+ def extension = project.extensions.create('configDocs', ConfigDocsExtension)
+
+ def sourceSets = project.extensions.getByType(SourceSetContainer)
+ def main = sourceSets.getByName('main')
+ def configDocs = sourceSets.create('configDocs')
+ configDocs.compileClasspath += main.output + main.compileClasspath
+ configDocs.runtimeClasspath += main.output + main.runtimeClasspath
+
+ // Build-time only tooling engine; kept off the production runtime classpath.
+ project.dependencies.add(
+ 'configDocsImplementation',
+ project.project(':jvm-libs:linea:config-docs'))
+
+ def specProvider = project.provider {
+ def spec = extension.spec
+ if (!spec) {
+ throw new IllegalStateException(
+ "configDocs.spec must be set to the fully-qualified ConfigDocsSpec class name")
+ }
+ spec
+ }
+
+ project.tasks.register('checkConfigDocs', JavaExec) {
+ it.group = 'verification'
+ it.description = 'Verifies that every config key is documented.'
+ it.classpath = configDocs.runtimeClasspath
+ it.mainClass.set('linea.config.docs.ConfigDocsCheckMain')
+ it.argumentProviders.add({ [specProvider.get()] } as org.gradle.process.CommandLineArgumentProvider)
+ }
+
+ project.tasks.register('generateConfigDocs', JavaExec) {
+ it.group = 'documentation'
+ it.description = 'Generates the config JSON schema snapshot and Markdown reference.'
+ it.classpath = configDocs.runtimeClasspath
+ it.mainClass.set('linea.config.docs.ConfigDocsGenerateMain')
+ it.workingDir = project.rootProject.projectDir
+ it.argumentProviders.add({ [specProvider.get()] } as org.gradle.process.CommandLineArgumentProvider)
+ }
+ }
+}
\ No newline at end of file
diff --git a/coordinator/app/build.gradle b/coordinator/app/build.gradle
index 2fa314cd87..2046a267d2 100644
--- a/coordinator/app/build.gradle
+++ b/coordinator/app/build.gradle
@@ -3,6 +3,13 @@ import org.gradle.api.tasks.testing.logging.TestLogEvent
plugins {
id 'net.consensys.zkevm.kotlin-application-conventions'
+ id 'linea.config-docs'
+}
+
+// Config documentation tooling: creates the `configDocs` source set and the
+// checkConfigDocs/generateConfigDocs tasks, driven by the spec below.
+configDocs {
+ spec = 'linea.coordinator.config.v2.docs.CoordinatorConfigDocsSpec'
}
configurations.configureEach {
exclude group: "org.rocksdb", module: "rocksdbjni"
diff --git a/coordinator/app/src/configDocs/kotlin/linea/coordinator/config/v2/docs/CoordinatorConfigDocsSpec.kt b/coordinator/app/src/configDocs/kotlin/linea/coordinator/config/v2/docs/CoordinatorConfigDocsSpec.kt
new file mode 100644
index 0000000000..70fbc4f14a
--- /dev/null
+++ b/coordinator/app/src/configDocs/kotlin/linea/coordinator/config/v2/docs/CoordinatorConfigDocsSpec.kt
@@ -0,0 +1,54 @@
+package linea.coordinator.config.v2.docs
+
+import linea.config.docs.ConfigDocsSpec
+import linea.config.docs.ConfigFileRoot
+import linea.config.docs.sectionByPackagePrefix
+import linea.coordinator.config.v2.toml.CoordinatorConfigFileToml
+import linea.coordinator.config.v2.toml.GasPriceCapTimeOfDayMultipliersConfigFileToml
+import linea.coordinator.config.v2.toml.SmartContractErrorCodesConfigFileToml
+import linea.coordinator.config.v2.toml.TracesLimitsConfigFileV4Toml
+import linea.coordinator.config.v2.toml.TracesLimitsConfigFileV5Toml
+
+/**
+ * Coordinator-specific configuration for the generic `config-docs` tooling. Lives in the
+ * `configDocs` source set (compiled against the config classes but kept out of the production
+ * jar) and is named from `coordinator/app/build.gradle` via `configDocs { spec = "…" }`.
+ */
+object CoordinatorConfigDocsSpec : ConfigDocsSpec {
+ /** Config data classes live in this package; used to distinguish nested sections from leaves. */
+ override val sectionDetector = sectionByPackagePrefix("linea.coordinator.config.v2.toml")
+
+ /** The Coordinator config files documented by this tooling, keyed by a stable label. */
+ override val files: List = listOf(
+ ConfigFileRoot(
+ label = "coordinator",
+ description = "Main Coordinator configuration.",
+ rootClass = CoordinatorConfigFileToml::class,
+ ),
+ ConfigFileRoot(
+ label = "traces-limits-v4",
+ description = "Per-module trace counter limits for v4 tracing modules.",
+ rootClass = TracesLimitsConfigFileV4Toml::class,
+ ),
+ ConfigFileRoot(
+ label = "traces-limits-v5",
+ description = "Per-module trace counter limits for v5 tracing modules.",
+ rootClass = TracesLimitsConfigFileV5Toml::class,
+ ),
+ ConfigFileRoot(
+ label = "gas-price-cap-time-of-day-multipliers",
+ description = "L1 dynamic gas price cap time-of-day multipliers.",
+ rootClass = GasPriceCapTimeOfDayMultipliersConfigFileToml::class,
+ ),
+ ConfigFileRoot(
+ label = "smart-contract-errors",
+ description = "Mapping of Linea smart-contract revert error codes to messages.",
+ rootClass = SmartContractErrorCodesConfigFileToml::class,
+ ),
+ )
+
+ override val jsonSchemaPath = "docs/tech/components/coordinator-config-schema.json"
+ override val markdownPath = "docs/tech/components/coordinator-config-reference.md"
+ override val markdownTitle = "Coordinator Configuration Reference"
+ override val regenerateCommand = "./gradlew :coordinator:app:generateConfigDocs"
+}