From 08b3efb4eb55b6d2d4f8eddb5d3e83ff6168faf4 Mon Sep 17 00:00:00 2001 From: Claw Date: Wed, 29 Jul 2026 22:31:50 -0700 Subject: [PATCH] F-115: applyTargetPlugins KSP companion deps + NiA Hilt Path A docs Forward processorConfigurationFeatures from host DSLs into applyTargetPlugins so targetPlugin companion .ksp deps create the ksp configuration (Hilt Path A). Document green external spike (hiltImpl/hiltApp/hiltAndroidUtil/hiltBinary). --- TICKETS.md | 4 +-- docs/DOGFOOD-NIA.md | 25 ++++++++++++++----- docs/PROGRESS.md | 21 ++++++++++++++++ docs/TARGET-PLUGINS.md | 6 +++++ plugins/android/src/main/java/androidApp.kt | 5 ++-- .../android/src/main/java/androidBinary.kt | 7 ++++-- plugins/android/src/main/java/androidUtil.kt | 5 ++-- plugins/android/src/main/java/impl.kt | 5 ++-- plugins/android/src/main/java/library.kt | 5 ++-- plugins/android/src/main/java/uiLibrary.kt | 5 ++-- .../deps/core/applyTargetPlugins.kt | 12 +++++++-- 11 files changed, 78 insertions(+), 22 deletions(-) diff --git a/TICKETS.md b/TICKETS.md index a1228e71..abc454f7 100644 --- a/TICKETS.md +++ b/TICKETS.md @@ -172,11 +172,11 @@ External multimodule OSS validation of meta-build axioms (structure over configu type-owned plugins, attrs-only call sites, closed matrix). **Not** another in-repo sample. Open coding: **F-115** (`in_progress`). **F-094** Portal remains `blocked` (human). -4h workers: pick F-115 phase C remainder (Hilt Path A / more features) unless blocked; do not `[SILENT]` while F-115 is open. +4h workers: pick F-115 phase C remainder (Room / Firebase / more features) unless blocked; do not `[SILENT]` while F-115 is open. | ID | Status | Title | Notes | |----|--------|-------|-------| -| F-115 | in_progress | Dogfood Now in Android under Forma | Primary OSS target [`android/nowinandroid`](https://github.com/android/nowinandroid). Design: [`docs/DOGFOOD-NIA.md`](docs/DOGFOOD-NIA.md). **Phase A+B+C partial done:** spike green with data/domain `androidUtil`, designsystem `uiLibrary`, topic ViewModel, interests feature (no impl→impl). Finding F11 (`compose=false` on non-UI androidUtil). **Next phase C:** Hilt Path A, Room/Firebase, more features. No `androidLibrary`. | +| F-115 | in_progress | Dogfood Now in Android under Forma | Primary OSS target [`android/nowinandroid`](https://github.com/android/nowinandroid). Design: [`docs/DOGFOOD-NIA.md`](docs/DOGFOOD-NIA.md). **Phase A+B+C Hilt done:** spike green with Hilt Path A (`hiltImpl`/`hiltApp`/`hiltAndroidUtil`/`hiltBinary`), data/domain, designsystem, topic+interests (no impl→impl). Engine: `applyTargetPlugins` + KSP companion deps (F12). **Next phase C:** Room/Firebase, more features. No `androidLibrary`. | ## Backlog (lower priority / historical GitHub) diff --git a/docs/DOGFOOD-NIA.md b/docs/DOGFOOD-NIA.md index 52639e2a..5072f0d4 100644 --- a/docs/DOGFOOD-NIA.md +++ b/docs/DOGFOOD-NIA.md @@ -1,11 +1,11 @@ # Dogfood: Now in Android → Forma (F-115) -**Status:** `in_progress` — phase C (data/domain/designsystem + interests) **green** +**Status:** `in_progress` — phase C Hilt Path A **green** (Room/more features next) **Upstream:** [android/nowinandroid](https://github.com/android/nowinandroid) (Apache-2.0) **Pinned checkout (local):** `/Users/claw/work/nowinandroid` @ `7d45eae` (main tip when cloned 2026-07-29) **Dogfood fork:** `/Users/claw/work/nowinandroid-forma` **Spike tree:** `/Users/claw/work/nowinandroid-forma/forma-spike` -**Forma plugins:** `mavenLocal` version **`0.1.3-NIA`** +**Forma plugins:** `mavenLocal` version **`0.1.3-NIA`** (republish after F-115 engine fix) **Goal:** Validate Forma’s meta-build approach on a **real multi-module OSS** graph — not another in-repo sample. ## Product bar (what “success” means) @@ -183,7 +183,7 @@ Workspace: **`/Users/claw/work/nowinandroid-forma/forma-spike`** (external consu **Real NiA DNA in spike:** `Topic` / `FollowableTopic` model sources; topic package namespace; topic strings moved to `androidRes`; Navigator port instead of Nav3 on api. -**Not yet (phase C remainder):** Hilt Path A; Room/Firebase; full upstream designsystem/core.ui; remaining features (foryou/bookmarks/search/settings); flavors. +**Not yet (phase C remainder):** Room/Firebase; full upstream designsystem/core.ui; remaining features (foryou/bookmarks/search/settings); flavors. ### Phase C — Expand features + cores (partial ✅ 2026-07-29) @@ -192,13 +192,24 @@ Workspace: same **`forma-spike`** external tree. | Step | Result | |------|--------| | Cores | `core-data-android-util` (`TopicsRepository` + in-memory), `core-domain-android-util` (`GetFollowableTopicsUseCase`), `core-designsystem-ui-library` (slim `NiaTheme` / loading / background) | -| Topic | `TopicViewModel` + designsystem-backed `TopicRoute` / `TopicScreen` (manual DI) | +| Topic | `TopicViewModel` + designsystem-backed `TopicRoute` / `TopicScreen` (manual DI first) | | Interests | `feature-interests-{api,res,impl}` — domain use case; navigates via **topic api** only | | Root | Manual DI graph + multi-destination Navigator adapter | | Verify | `./gradlew :binary:assembleDebug` → **BUILD SUCCESSFUL** (198 tasks); APK ~9.2 MB | | Finding F11 | Project-global `compose=true` forces Compose compiler on `androidUtil` unless `compose = false` — set on data/domain | -**Still open in phase C:** Hilt Path A (type-owned like Metro F-095), Room derived type, Firebase binary, foryou/bookmarks/search/settings, full designsystem port. +### Phase C — Hilt Path A ✅ (2026-07-29) + +| Step | Result | +|------|--------| +| `forma-defs` | Local composite (`tools.forma.nia:forma-defs:0.0.1`) — Path A `registerTargetPlugin` on `impl` / `app` / `androidUtil` / `binary` + thin `hiltImpl` / `hiltApp` / `hiltAndroidUtil` / `hiltBinary` | +| Classpath | `extraPlugins`: forma-defs + `hilt-android-gradle-plugin` 2.59 + KSP 2.3.10 (classpath only) | +| Sources | `@HiltAndroidApp` on **binary**; `@AndroidEntryPoint` MainActivity; `@HiltViewModel` + assisted topicId; data `@Binds` module; domain `@Inject` use case | +| Engine fix | `applyTargetPlugins` now forwards `processorConfigurationFeatures` so companion `.ksp` deps create `ksp` config (was empty → “Configuration with name 'ksp' not found”) | +| F11+ | `compose = false` on data/domain **and** binary Application host (Compose compiler without runtime ICE) | +| Verify | `forma-spike` `./gradlew :binary:assembleDebug` → **BUILD SUCCESSFUL** (242 tasks); Hilt tasks `hiltAggregateDepsDebug` / `hiltJavaCompileDebug` green | + +**Still open in phase C:** Room derived type, Firebase binary, foryou/bookmarks/search/settings, full designsystem port. ### Phase D — Case study write-up @@ -217,7 +228,9 @@ Workspace: same **`forma-spike`** external tree. | F8 | 2026-07-29 | upstream topic **api** ships `res/strings` | **Spike:** `feature/topic/res` `androidRes`; `api` has no `res/` (matrix content rule) | | F9 | 2026-07-29 | `tools.forma.includer` **not** published to mavenLocal | External consumer used **flat** `include(":feature-topic-api")` + `projectDir`; Forma `target(":feature:topic:api")` → `:feature-topic-api`. Product gap: publish includer or document flat-name recipe in PLUGIN-PUBLISH | | F10 | 2026-07-29 | First target() resolve failed with nested `:feature:topic:api` includes | Confirmed path mapping; flat names required without includer | -| F11 | 2026-07-29 | project-global `compose=true` + `androidUtil` | Compose compiler runs on data/domain without Compose runtime → ICE. **Fix:** `androidUtil(..., compose = false)` on non-UI cores | +| F11 | 2026-07-29 | project-global `compose=true` + `androidUtil` | Compose compiler runs on data/domain without Compose runtime → ICE. **Fix:** `androidUtil(..., compose = false)` on non-UI cores; same on binary when only Application sources | +| F12 | 2026-07-29 | Path A companion `.ksp` deps | `applyTargetPlugins` called `applyDependencies` **without** `configurationFeatures` → no `ksp` configuration. **Engine fix:** optional `configurationFeatures` param forwarded from DSLs that already know processors (`impl`/`app`/`androidUtil`/`binary`/…) | +| F13 | 2026-07-29 | Hilt Application placement | `@HiltAndroidApp` must live on `com.android.application` (`androidBinary` / `hiltBinary`), not library-shell `androidApp` | ## Local reference commands diff --git a/docs/PROGRESS.md b/docs/PROGRESS.md index 2d6e9077..90c6b376 100644 --- a/docs/PROGRESS.md +++ b/docs/PROGRESS.md @@ -2,6 +2,27 @@ Newest entries first. +## 2026-07-29 — F-115: NiA dogfood Hilt Path A + applyTargetPlugins KSP fix + +- **Ticket:** F-115 still `in_progress` (Room / more features next) +- **Skills/modes:** Hermes direct dogfood + small engine fix (target-plugin companion KSP) +- **Engine (`plugins/`):** + - `applyTargetPlugins(type, configurationFeatures=…)` forwards processor map + - Wired on `impl` / `androidApp` / `androidUtil` / `uiLibrary` / `library` / `androidBinary` + - Republished mavenLocal **`0.1.3-NIA`** +- **External tree:** `/Users/claw/work/nowinandroid-forma/forma-spike` + - `forma-defs` Path A: Hilt on impl/app/androidUtil/binary + thin DSLs + - `@HiltAndroidApp` on binary; `@AndroidEntryPoint` root-app; `@HiltViewModel` features + - data `@Binds` + domain `@Inject`; interests → topic **api** only + - Findings F12 (KSP companion), F13 (Application on binary), F11 extended to binary +- **Verify (real host):** + - `plugins/` `:deps:test` + `publishAllToMavenLocal -PformaLocalVersion=0.1.3-NIA` → **BUILD SUCCESSFUL** + - `forma-spike` `./gradlew :binary:assembleDebug` → **BUILD SUCCESSFUL** (242 tasks; Hilt aggregate/compile green) +- **Docs:** `docs/DOGFOOD-NIA.md` phase C Hilt; TICKETS F-115 notes; spike README +- **Commits/PRs:** this branch → PR base `v2` +- **Blockers:** none product; F-094 Portal still human-blocked +- **Next:** Room Path B derived type and/or more NiA features + ## 2026-07-29 — F-115: NiA dogfood phase C (data/domain/designsystem + interests) - **Ticket:** F-115 still `in_progress` (Hilt Path A / Room / remaining features next) diff --git a/docs/TARGET-PLUGINS.md b/docs/TARGET-PLUGINS.md index 00fd7f97..40891f96 100644 --- a/docs/TARGET-PLUGINS.md +++ b/docs/TARGET-PLUGINS.md @@ -24,6 +24,12 @@ plugin on every call site of that type. - **Path A:** `registerTargetPlugin(AndroidTargetTypes.res, …)` only if *every* module of that kind should get it. 3. Call sites set **attributes only** — never plugin ids, never `.withPlugin`. +**Companion deps + KSP (F-115):** `targetPlugin(id, dependencies = …)` may include +`.ksp` processor GAVs. `applyTargetPlugins` forwards the host DSL’s +`processorConfigurationFeatures` so the `ksp` configuration exists before those +deps are added (Hilt Path A / Room Path B). Republish plugins after pulling this +engine fix when dogfooding from `mavenLocal`. + Hands-on: [`examples/android/10-target-plugins`](../examples/android/10-target-plugins) (safe-args) · [`examples/android/11-metro-di`](../examples/android/11-metro-di) (Metro DI). Agent skill: [`forma-target-plugins`](../examples/agent-skills/forma-target-plugins.md). diff --git a/plugins/android/src/main/java/androidApp.kt b/plugins/android/src/main/java/androidApp.kt index 6e45fd39..30225a3a 100644 --- a/plugins/android/src/main/java/androidApp.kt +++ b/plugins/android/src/main/java/androidApp.kt @@ -63,13 +63,14 @@ fun Project.androidApp( androidLibraryFeatureDefinition(libraryFeatureConfiguration), kotlinAndroidFeatureDefinition() ) - applyTargetPlugins(AndroidTargetTypes.app) + val processors = processorConfigurationFeatures() + applyTargetPlugins(AndroidTargetTypes.app, configurationFeatures = processors) applyDependencies( validator = AndroidTargetRegistry.validatorFor(AndroidTargetTypes.app).asValidator(), dependencies = dependencies, testDependencies = testDependencies, androidTestDependencies = androidTestDependencies, - configurationFeatures = processorConfigurationFeatures() + configurationFeatures = processors ) } diff --git a/plugins/android/src/main/java/androidBinary.kt b/plugins/android/src/main/java/androidBinary.kt index 5c47221a..d3bb312d 100644 --- a/plugins/android/src/main/java/androidBinary.kt +++ b/plugins/android/src/main/java/androidBinary.kt @@ -1,6 +1,7 @@ import org.gradle.api.Project import tools.forma.android.feature.AndroidBinaryFeatureConfiguration import tools.forma.android.feature.androidBinaryFeatureDefinition +import tools.forma.android.feature.processorConfigurationFeatures import tools.forma.android.feature.applyFeatures import tools.forma.android.target.AndroidTargetRegistry import tools.forma.android.target.AndroidTargetTypes @@ -88,10 +89,12 @@ fun Project.androidBinary( applyFeatures( androidBinaryFeatureDefinition(binaryFeatureConfiguration) ) - applyTargetPlugins(AndroidTargetTypes.binary) + val processors = processorConfigurationFeatures() + applyTargetPlugins(AndroidTargetTypes.binary, configurationFeatures = processors) applyDependencies( validator = AndroidTargetRegistry.validatorFor(AndroidTargetTypes.binary).asValidator(), - dependencies = dependencies + dependencies = dependencies, + configurationFeatures = processors, ) } diff --git a/plugins/android/src/main/java/androidUtil.kt b/plugins/android/src/main/java/androidUtil.kt index 96e459ca..89889b45 100644 --- a/plugins/android/src/main/java/androidUtil.kt +++ b/plugins/android/src/main/java/androidUtil.kt @@ -59,12 +59,13 @@ fun Project.androidUtil( androidLibraryFeatureDefinition(androidFeatureConfig), kotlinAndroidFeatureDefinition() ) - applyTargetPlugins(AndroidTargetTypes.androidUtil) + val processors = processorConfigurationFeatures() + applyTargetPlugins(AndroidTargetTypes.androidUtil, configurationFeatures = processors) applyDependencies( validator = AndroidTargetRegistry.validatorFor(AndroidTargetTypes.androidUtil).asValidator(), dependencies = dependencies, testDependencies = testDependencies, - configurationFeatures = processorConfigurationFeatures() + configurationFeatures = processors ) } diff --git a/plugins/android/src/main/java/impl.kt b/plugins/android/src/main/java/impl.kt index 49b1aebd..922cc911 100644 --- a/plugins/android/src/main/java/impl.kt +++ b/plugins/android/src/main/java/impl.kt @@ -65,13 +65,14 @@ fun Project.impl( androidLibraryFeatureDefinition(libraryFeatureConfiguration), kotlinAndroidFeatureDefinition() ) - applyTargetPlugins(AndroidTargetTypes.impl) + val processors = processorConfigurationFeatures() + applyTargetPlugins(AndroidTargetTypes.impl, configurationFeatures = processors) applyDependencies( validator = AndroidTargetRegistry.validatorFor(AndroidTargetTypes.impl).asValidator(), dependencies = dependencies, testDependencies = testDependencies, androidTestDependencies = androidTestDependencies, - configurationFeatures = processorConfigurationFeatures() + configurationFeatures = processors ) } diff --git a/plugins/android/src/main/java/library.kt b/plugins/android/src/main/java/library.kt index 3e1841c7..dc226483 100644 --- a/plugins/android/src/main/java/library.kt +++ b/plugins/android/src/main/java/library.kt @@ -31,12 +31,13 @@ fun Project.library( applyFeatures( kotlinFeatureDefinition() ) - applyTargetPlugins(AndroidTargetTypes.jvmLibrary) + val processors = processorConfigurationFeatures() + applyTargetPlugins(AndroidTargetTypes.jvmLibrary, configurationFeatures = processors) applyDependencies( validator = AndroidTargetRegistry.validatorFor(AndroidTargetTypes.jvmLibrary).asValidator(), dependencies = dependencies, testDependencies = testDependencies, - configurationFeatures = processorConfigurationFeatures() + configurationFeatures = processors ) } diff --git a/plugins/android/src/main/java/uiLibrary.kt b/plugins/android/src/main/java/uiLibrary.kt index 23c5ec46..f2532e3f 100644 --- a/plugins/android/src/main/java/uiLibrary.kt +++ b/plugins/android/src/main/java/uiLibrary.kt @@ -51,14 +51,15 @@ fun Project.uiLibrary( androidLibraryFeatureDefinition(libraryFeatureConfiguration), kotlinAndroidFeatureDefinition() ) - applyTargetPlugins(AndroidTargetTypes.uiLibrary) + val processors = processorConfigurationFeatures() + applyTargetPlugins(AndroidTargetTypes.uiLibrary, configurationFeatures = processors) applyDependencies( validator = AndroidTargetRegistry.validatorFor(AndroidTargetTypes.uiLibrary).asValidator(), dependencies = dependencies, testDependencies = testDependencies, androidTestDependencies = androidTestDependencies, - configurationFeatures = processorConfigurationFeatures() + configurationFeatures = processors ) } diff --git a/plugins/deps/src/main/java/tools.forma/deps/core/applyTargetPlugins.kt b/plugins/deps/src/main/java/tools.forma/deps/core/applyTargetPlugins.kt index ac9c206c..318aa1eb 100644 --- a/plugins/deps/src/main/java/tools.forma/deps/core/applyTargetPlugins.kt +++ b/plugins/deps/src/main/java/tools.forma/deps/core/applyTargetPlugins.kt @@ -12,8 +12,15 @@ import tools.forma.validation.EmptyValidator * Plugins are looked up from the global TargetPluginRegistry (populated via * registerTargetPlugin or deriveTargetType). This is the mechanism that makes * "type owns plugin, call site has zero plugin surface". + * + * [configurationFeatures] must mirror the host DSL's processor map (e.g. KSP) + * so companion deps that use [Ksp] / custom configurations can create those + * configurations before [applyDependencies] adds them (F-115 Hilt Path A). */ -fun Project.applyTargetPlugins(type: TargetType) { +fun Project.applyTargetPlugins( + type: TargetType, + configurationFeatures: Map Unit> = emptyMap(), +) { val specs = TargetPluginRegistry.get(type) specs.forEach { spec -> apply(plugin = spec.id) @@ -23,7 +30,8 @@ fun Project.applyTargetPlugins(type: TargetType) { applyDependencies( validator = EmptyValidator, dependencies = deps, - repositoriesConfiguration = EmptyRepositoriesConfiguration + repositoriesConfiguration = EmptyRepositoriesConfiguration, + configurationFeatures = configurationFeatures, ) } }