Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
4 changes: 2 additions & 2 deletions TICKETS.md
Original file line number Diff line number Diff line change
Expand Up @@ -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)

Expand Down
25 changes: 19 additions & 6 deletions docs/DOGFOOD-NIA.md
Original file line number Diff line number Diff line change
@@ -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)
Expand Down Expand Up @@ -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)

Expand All @@ -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

Expand All @@ -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

Expand Down
21 changes: 21 additions & 0 deletions docs/PROGRESS.md
Original file line number Diff line number Diff line change
Expand Up @@ -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)
Expand Down
6 changes: 6 additions & 0 deletions docs/TARGET-PLUGINS.md
Original file line number Diff line number Diff line change
Expand Up @@ -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).
Expand Down
5 changes: 3 additions & 2 deletions plugins/android/src/main/java/androidApp.kt
Original file line number Diff line number Diff line change
Expand Up @@ -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
)
}
7 changes: 5 additions & 2 deletions plugins/android/src/main/java/androidBinary.kt
Original file line number Diff line number Diff line change
@@ -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
Expand Down Expand Up @@ -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,
)
}
5 changes: 3 additions & 2 deletions plugins/android/src/main/java/androidUtil.kt
Original file line number Diff line number Diff line change
Expand Up @@ -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
)
}
5 changes: 3 additions & 2 deletions plugins/android/src/main/java/impl.kt
Original file line number Diff line number Diff line change
Expand Up @@ -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
)
}
5 changes: 3 additions & 2 deletions plugins/android/src/main/java/library.kt
Original file line number Diff line number Diff line change
Expand Up @@ -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
)
}
5 changes: 3 additions & 2 deletions plugins/android/src/main/java/uiLibrary.kt
Original file line number Diff line number Diff line change
Expand Up @@ -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
)
}

Original file line number Diff line number Diff line change
Expand Up @@ -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<ConfigurationType, () -> Unit> = emptyMap(),
) {
val specs = TargetPluginRegistry.get(type)
specs.forEach { spec ->
apply(plugin = spec.id)
Expand All @@ -23,7 +30,8 @@ fun Project.applyTargetPlugins(type: TargetType) {
applyDependencies(
validator = EmptyValidator,
dependencies = deps,
repositoriesConfiguration = EmptyRepositoriesConfiguration
repositoriesConfiguration = EmptyRepositoriesConfiguration,
configurationFeatures = configurationFeatures,
)
}
}
Expand Down
Loading