From 5f2e7b87c996c1f535aadb5fabb60424fd371454 Mon Sep 17 00:00:00 2001 From: Jakub Zych Date: Thu, 5 Feb 2026 13:29:45 +0100 Subject: [PATCH] fix(02): revise plans based on checker feedback - 02-01 Task 3: Add concrete REPL verification command for PluginManifest.parse - 02-02 Task 1: Add FieldDef case class to placeholder types in PluginRegistration.scala - 02-02 Task 1: Document PluginEnv as placeholder to be expanded in 02-03 - 02-03 Task 3: Remove duplicate Main.scala code, keep only corrected version - 02-03: Add SummerPlugin.scala to files_modified for PluginEnv update --- .../phases/02-plugin-system/02-01-PLAN.md | 20 +++- .../phases/02-plugin-system/02-02-PLAN.md | 15 ++- .../phases/02-plugin-system/02-03-PLAN.md | 112 ++++++------------ 3 files changed, 68 insertions(+), 79 deletions(-) diff --git a/.planning/phases/02-plugin-system/02-01-PLAN.md b/.planning/phases/02-plugin-system/02-01-PLAN.md index 34453c9..a968ebd 100644 --- a/.planning/phases/02-plugin-system/02-01-PLAN.md +++ b/.planning/phases/02-plugin-system/02-01-PLAN.md @@ -282,7 +282,25 @@ dependencies: golem15.user: "^1.0.0" ``` -Then verify parsing works by checking the code compiles and the types are correct. +Then test discovery works by running this command in the Mill REPL: +```bash +./mill -i summercms.console +``` + +Once in the REPL, verify parsing: +```scala +import plugin._ +val yaml = """ +vendor: test +name: sample +version: 1.0.0 +description: Test plugin +""" +val result = PluginManifest.parse(yaml) +println(result) // Should print Right(PluginManifest(test,sample,1.0.0,...)) +``` + +Exit REPL with `:quit` after verification. PluginManifest parses YAML manifests, PluginDiscovery scans plugins/ directory, plugins/ directory exists diff --git a/.planning/phases/02-plugin-system/02-02-PLAN.md b/.planning/phases/02-plugin-system/02-02-PLAN.md index efa9404..ee57da9 100644 --- a/.planning/phases/02-plugin-system/02-02-PLAN.md +++ b/.planning/phases/02-plugin-system/02-02-PLAN.md @@ -136,7 +136,9 @@ case class PluginRegistration( // Event subscriptions (Phase 2 extension API) events: List[EventSubscription] = List.empty, // Extensions to other plugins (Phase 2 extension API) - extensions: List[ExtensionDef] = List.empty + extensions: List[ExtensionDef] = List.empty, + // Form field definitions (for YAML-driven forms) + fields: List[FieldDef] = List.empty ) object PluginRegistration: @@ -149,6 +151,7 @@ case class NavigationDef(id: String, label: String, url: String, icon: String = case class SettingDef(key: String, label: String, icon: String = "") case class EventSubscription(eventType: String, handler: String) case class ExtensionDef(target: String, extensionClass: String) +case class FieldDef(name: String, fieldType: String) ``` **SummerPlugin.scala:** @@ -172,6 +175,9 @@ trait SummerPlugin: * Async boot phase - can perform effects. * Called after all dependencies have booted. * Use for: database setup, event subscriptions, service initialization. + * + * PluginEnv is a placeholder that will be expanded in 02-03 to include + * EventService and ExtensionRegistry when the extension API is implemented. */ def boot: ZIO[PluginEnv, PluginError, Unit] = ZIO.unit @@ -181,12 +187,15 @@ trait SummerPlugin: */ def shutdown: ZIO[Any, Nothing, Unit] = ZIO.unit -/** Environment available to plugins during boot */ +/** + * Environment available to plugins during boot. + * Initially just PluginContext; extended in 02-03 to include EventService & ExtensionRegistry. + */ type PluginEnv = PluginContext ``` Run `./mill summercms.compile` - all types compile without errors - PluginState enum with all lifecycle states, PluginContext, PluginRegistration with placeholder defs, SummerPlugin trait + PluginState enum with all lifecycle states, PluginContext, PluginRegistration with placeholder defs including FieldDef, SummerPlugin trait diff --git a/.planning/phases/02-plugin-system/02-03-PLAN.md b/.planning/phases/02-plugin-system/02-03-PLAN.md index 6942fc8..2c97ef9 100644 --- a/.planning/phases/02-plugin-system/02-03-PLAN.md +++ b/.planning/phases/02-plugin-system/02-03-PLAN.md @@ -9,6 +9,7 @@ files_modified: - summercms/src/plugin/ExtensionRegistry.scala - summercms/src/plugin/SummerEvent.scala - summercms/src/plugin/package.scala + - summercms/src/plugin/SummerPlugin.scala - summercms/src/Main.scala autonomous: true @@ -311,7 +312,7 @@ trait BlogExtension: /** Validate post data */ def validate(data: Map[String, Any]): Either[String, Unit] = Right(()) -// FieldDef is already defined in PluginRegistration.scala, reuse it +// FieldDef is defined in PluginRegistration.scala ``` Run `./mill summercms.compile` - ExtensionRegistry compiles. It provides type-safe extension registration keyed by ClassTag. @@ -319,9 +320,10 @@ trait BlogExtension: - Task 3: Create plugin package exports and integrate with Main + Task 3: Create plugin package exports, update PluginEnv, and integrate with Main summercms/src/plugin/package.scala + summercms/src/plugin/SummerPlugin.scala summercms/src/Main.scala @@ -340,87 +342,47 @@ package object plugin: type PluginBootEnv = PluginContext & EventService & ExtensionRegistry ``` -**Update Main.scala** to integrate the plugin system: +**Update SummerPlugin.scala** to use the expanded PluginEnv: + +Read the existing file from 02-02 and update ONLY the PluginEnv type alias at the bottom: + ```scala +package plugin + import zio.* -import zio.http.* -import zio.config.typesafe.TypesafeConfigProvider -import api.Routes -import _root_.config.{AppConfig as SummerConfig} -import db.QuillContext -import plugin.{PluginManager, PluginDiscovery, pluginLayer} +/** Base trait for all SummerCMS plugins */ +trait SummerPlugin: + /** Plugin identifier - must match manifest */ + def id: PluginId -object Main extends ZIOAppDefault { + /** + * Synchronous registration phase - pure data, no effects. + * Called during discovery to collect plugin declarations. + */ + def register(ctx: PluginContext): PluginRegistration = PluginRegistration.empty - private val banner: String = - """ - | - | | . - | `. * | .' - | `. ._|_* .' . - | . * .' `. * - | -------| |------- - | . *`.___.' * . - | .' |* `. * - | .' * | . `. - | . | - | - | S U M M E R C M S - |""".stripMargin + /** + * Async boot phase - can perform effects. + * Called after all dependencies have booted. + * Use for: database setup, event subscriptions, service initialization. + */ + def boot: ZIO[PluginEnv, PluginError, Unit] = ZIO.unit - override val bootstrap: ZLayer[ZIOAppArgs, Any, Any] = - Runtime.setConfigProvider(TypesafeConfigProvider.fromResourcePath()) + /** + * Async shutdown phase - cleanup resources. + * Called in reverse dependency order during application shutdown. + */ + def shutdown: ZIO[Any, Nothing, Unit] = ZIO.unit - override def run: ZIO[Any, Any, Any] = - for { - cfg <- ZIO.config[SummerConfig](SummerConfig.config) - _ <- Console.printLine(banner) - _ <- Console.printLine(s"Starting on port ${cfg.server.port}...") - _ <- Console.printLine("") - // Initialize plugin system - _ <- Console.printLine("Loading plugins...") - _ <- PluginManager.loadPlugins(PluginDiscovery.defaultPluginsDir) - .catchAll(e => Console.printLine(s"Plugin loading warning: ${e.message}").as(())) - _ <- PluginManager.bootAll - .catchAll(e => Console.printLine(s"Plugin boot warning: ${e.message}").as(())) - plugins <- PluginManager.listPlugins - _ <- Console.printLine(s"Loaded ${plugins.count(_._2.isActive)} plugin(s)") - _ <- Console.printLine("") - // Note: Migrations are NOT auto-run. Use CLI to run migrations (Phase 5). - _ <- Server.serve(Routes.routes).provide( - Server.defaultWithPort(cfg.server.port), - QuillContext.dataSourceLayer - ) - } yield () - - // Provide plugin layers - override def run: ZIO[Any, Any, Any] = - (for { - cfg <- ZIO.config[SummerConfig](SummerConfig.config) - _ <- Console.printLine(banner) - _ <- Console.printLine(s"Starting on port ${cfg.server.port}...") - _ <- Console.printLine("") - // Initialize plugin system - _ <- Console.printLine("Loading plugins...") - _ <- PluginManager.loadPlugins(PluginDiscovery.defaultPluginsDir) - .catchAll(e => Console.printLine(s"Plugin loading warning: ${e.message}").as(())) - _ <- PluginManager.bootAll - .catchAll(e => Console.printLine(s"Plugin boot warning: ${e.message}").as(())) - plugins <- PluginManager.listPlugins - _ <- Console.printLine(s"Loaded ${plugins.count(_._2.isActive)} plugin(s)") - _ <- Console.printLine("") - // Note: Migrations are NOT auto-run. Use CLI to run migrations (Phase 5). - _ <- Server.serve(Routes.routes).provide( - Server.defaultWithPort(cfg.server.port), - QuillContext.dataSourceLayer - ) - } yield ()).provide(pluginLayer) +/** + * Environment available to plugins during boot. + * Includes context, events, and extension registry for full plugin capabilities. + */ +type PluginEnv = PluginContext & EventService & ExtensionRegistry ``` -Wait - the above has a duplicate `def run`. Let me provide the correct version: - -**Main.scala (corrected full file):** +**Main.scala** (complete replacement): ```scala import zio.* import zio.http.* @@ -483,7 +445,7 @@ object Main extends ZIOAppDefault { Run `./mill summercms.compile` - Main.scala compiles with plugin integration. Run `./mill summercms.run` - Server starts, logs "Loading plugins..." and "Loaded 0 plugin(s)" (since no plugins exist yet). - Plugin package exports combined layer, Main.scala loads and boots plugins on startup + Plugin package exports combined layer, PluginEnv updated to include EventService and ExtensionRegistry, Main.scala loads and boots plugins on startup