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 (Room / Firebase / more features) unless blocked; do not `[SILENT]` while F-115 is open.
4h workers: pick F-115 phase C remainder (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 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`. |
| 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+Room done:** spike green with Hilt Path A + Room Path B (`roomAndroidUtil` / `androidUtilTarget`), data OfflineFirst+DAO, topic+interests (no impl→impl). Engine: KSP companion (F12) + `androidUtilTarget` (F15). **Next phase C:** Firebase binary, more features. No `androidLibrary`. |

## Backlog (lower priority / historical GitHub)

Expand Down
4 changes: 3 additions & 1 deletion docs/CALL-SITE-SURFACE.md
Original file line number Diff line number Diff line change
Expand Up @@ -45,7 +45,9 @@ All return **`Unit`**.
| `androidNative` | `native` | `buildSystem`, `abi`, … | NDK leaf; consumers `androidUtil`/`app`/`binary` |
| Path B derived (e.g. `navigationRes`) | often shared suffix | same as base + type-owned plugins | Consumer-defined; see TARGET-PLUGINS |

Internal helper `resourcesTarget(type, …)` backs `androidRes` and derived res DSLs —
Internal helpers: `resourcesTarget(type, …)` backs `androidRes` and derived res DSLs;
`androidUtilTarget(type, …)` backs `androidUtil` and derived util DSLs (e.g. dogfood
`roomAndroidUtil`) —
not a public “generic library” escape hatch.

## JVM target DSLs (`tools.forma.jvm`)
Expand Down
22 changes: 19 additions & 3 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 Hilt Path A **green** (Room/more features next)
**Status:** `in_progress` — phase C Hilt Path A + **Room Path B green** (Firebase / 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`** (republish after F-115 engine fix)
**Forma plugins:** `mavenLocal` version **`0.1.3-NIA`** (republish after `androidUtilTarget` + prior KSP 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 @@ -209,7 +209,21 @@ Workspace: same **`forma-spike`** external tree.
| 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.
**Still open in phase C (pre-Room):** Firebase binary, foryou/bookmarks/search/settings, full designsystem port.

### Phase C — Room Path B ✅ (2026-07-30)

| Step | Result |
|------|--------|
| Engine | `androidUtilTarget(type, …)` — Path B twin of `resourcesTarget`; `androidUtil` delegates |
| `forma-defs` | `RoomAndroidUtilType` = `deriveTargetType(base=androidUtil, suffix=android-util)` + thin `roomAndroidUtil` |
| Plugins on type | `androidx.room` (runtime/ktx + `room-compiler`.ksp) **and** Hilt re-bound (F14: derived types do not inherit base Path A plugins) |
| Module | `core-database-android-util` — slim `NiaDatabase` / `TopicEntity` / `TopicDao` / Hilt `DatabaseModule`; `exportSchema=true` → `schemas/…/1.json` |
| Data | `OfflineFirstTopicsRepository` seeds DAO when empty; UserData still in-memory |
| Classpath | `extraPlugins` += Room Gradle plugin (`libs.plugins.room`) |
| Verify | `forma-spike` `./gradlew :binary:assembleDebug` → **BUILD SUCCESSFUL** (266 tasks); KSP `TopicDao_Impl` / `NiaDatabase_Impl`; APK ~10 MB |

**Still open in phase C:** Firebase binary, remaining features, full designsystem, DataStore UserData.

### Phase D — Case study write-up

Expand All @@ -231,6 +245,8 @@ Workspace: same **`forma-spike`** external tree.
| 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` |
| F14 | 2026-07-30 | Path B does not inherit base type plugins | `deriveTargetType` clones matrix/content rules only; plugin registry is per-type. Room util must **re-register Hilt** (or stack plugins on the derived type) — not rely on Path A `androidUtil` Hilt alone |
| F15 | 2026-07-30 | No public `androidUtil(type=)` before helper | Engine needed `androidUtilTarget` (like `resourcesTarget`) so Path B Room DSL can pass derived type + processor features |

## Local reference commands

Expand Down
23 changes: 23 additions & 0 deletions docs/PROGRESS.md
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,29 @@

Newest entries first.

## 2026-07-30 — F-115: NiA dogfood Room Path B + androidUtilTarget

- **Ticket:** F-115 still `in_progress` (Firebase / more features next)
- **Skills/modes:** Hermes direct dogfood + mechanical Path B engine helper (F-050/F-115 class)
- **Engine (`plugins/android`):**
- New `androidUtilTarget(type, …)` — shared AGP/util wiring + `applyTargetPlugins` with processor features
- `androidUtil` delegates to it with `AndroidTargetTypes.androidUtil`
- Republished mavenLocal **`0.1.3-NIA`**
- **External tree:** `/Users/claw/work/nowinandroid-forma/forma-spike`
- `forma-defs/RoomAndroidUtil.kt` — Path B `deriveTargetType` + `roomAndroidUtil` thin DSL
- `core-database-android-util` — Room DB/DAO/entity + Hilt provides; schema `1.json` exported
- data: `OfflineFirstTopicsRepository` seeds DAO; still no impl→impl
- Findings **F14** (derived types need Hilt re-bind), **F15** (`androidUtilTarget` helper)
- **Verify (real host):**
- `scripts/publish-local.sh 0.1.3-NIA` → **ok**
- `forma-spike` `./gradlew :binary:assembleDebug` → **BUILD SUCCESSFUL** (266 tasks)
- KSP generated `TopicDao_Impl` / `NiaDatabase_Impl`; Room schema JSON present
- APK `binary-debug.apk` ~10.2 MB
- **Docs:** `docs/DOGFOOD-NIA.md` phase C Room; TICKETS F-115 notes; TARGET-PLUGINS pointer; spike README
- **Commits/PRs:** this branch → PR base `v2`
- **Blockers:** none product; F-094 Portal still human-blocked
- **Next:** Firebase Path A on binary and/or more NiA features

## 2026-07-29 — F-115: NiA dogfood Hilt Path A + applyTargetPlugins KSP fix

- **Ticket:** F-115 still `in_progress` (Room / more features next)
Expand Down
2 changes: 2 additions & 0 deletions docs/TARGET-PLUGINS.md
Original file line number Diff line number Diff line change
Expand Up @@ -32,6 +32,8 @@ 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).
**Dogfood Path B util:** NiA spike `roomAndroidUtil` via engine helper `androidUtilTarget`
(same role as `resourcesTarget` for res) — see [`DOGFOOD-NIA.md`](DOGFOOD-NIA.md).
Agent skill: [`forma-target-plugins`](../examples/agent-skills/forma-target-plugins.md).

Global configuration lives at root `buildscript { androidProjectConfiguration(...) }` (F-082).
Expand Down
39 changes: 6 additions & 33 deletions plugins/android/src/main/java/androidUtil.kt
Original file line number Diff line number Diff line change
@@ -1,22 +1,10 @@
import tools.forma.android.feature.AndroidLibraryFeatureConfiguration
import tools.forma.android.feature.androidLibraryFeatureDefinition
import tools.forma.android.feature.applyFeatures
import tools.forma.android.feature.kotlinAndroidFeatureDefinition
import tools.forma.android.target.AndroidTargetRegistry
import tools.forma.android.target.AndroidTargetTypes
import tools.forma.owners.NoOwner
import tools.forma.owners.Owner
import tools.forma.android.validation.disallowResources
import tools.forma.android.visibility.Public
import tools.forma.android.visibility.Visibility
import org.gradle.api.Project
import tools.forma.android.feature.processorConfigurationFeatures
import tools.forma.deps.core.applyDependencies
import tools.forma.deps.core.applyTargetPlugins
import tools.forma.deps.core.FormaDependency
import tools.forma.deps.fleet.registerFormaLayout
import tools.forma.validation.asValidator
import tools.forma.validation.validate

/**
* TODO
Expand All @@ -33,6 +21,7 @@ import tools.forma.validation.validate
* )
* ```
*
* Path B derived util kinds (Room, etc.) use [androidUtilTarget] with a derived type.
*/
fun Project.androidUtil(
packageName: String,
Expand All @@ -43,29 +32,13 @@ fun Project.androidUtil(
/** Enable Jetpack Compose; defaults to project-wide `compose` setting. */
compose: Boolean = Forma.settings.compose,
) {

disallowResources()

//TODO unify with util, use androidJar dependency
AndroidTargetRegistry.selfValidator(AndroidTargetTypes.androidUtil).asValidator().validate(target)
registerFormaLayout(packageName)

val androidFeatureConfig = AndroidLibraryFeatureConfiguration(
androidUtilTarget(
type = AndroidTargetTypes.androidUtil,
packageName = packageName,
compose = compose,
)

applyFeatures(
androidLibraryFeatureDefinition(androidFeatureConfig),
kotlinAndroidFeatureDefinition()
)
val processors = processorConfigurationFeatures()
applyTargetPlugins(AndroidTargetTypes.androidUtil, configurationFeatures = processors)

applyDependencies(
validator = AndroidTargetRegistry.validatorFor(AndroidTargetTypes.androidUtil).asValidator(),
owner = owner,
visibility = visibility,
dependencies = dependencies,
testDependencies = testDependencies,
configurationFeatures = processors
compose = compose,
)
}
62 changes: 62 additions & 0 deletions plugins/android/src/main/java/androidUtilTarget.kt
Original file line number Diff line number Diff line change
@@ -0,0 +1,62 @@
import tools.forma.android.feature.AndroidLibraryFeatureConfiguration
import tools.forma.android.feature.androidLibraryFeatureDefinition
import tools.forma.android.feature.applyFeatures
import tools.forma.android.feature.kotlinAndroidFeatureDefinition
import tools.forma.android.feature.processorConfigurationFeatures
import tools.forma.android.target.AndroidTargetRegistry
import tools.forma.android.validation.disallowResources
import tools.forma.android.visibility.Public
import tools.forma.android.visibility.Visibility
import org.gradle.api.Project
import tools.forma.core.target.TargetType
import tools.forma.deps.core.applyDependencies
import tools.forma.deps.core.applyTargetPlugins
import tools.forma.deps.core.FormaDependency
import tools.forma.deps.fleet.registerFormaLayout
import tools.forma.owners.NoOwner
import tools.forma.owners.Owner
import tools.forma.validation.asValidator
import tools.forma.validation.validate

/**
* Non-UI Android library target bound to an arbitrary [TargetType].
*
* Used by [androidUtil] (pre-defined `android-util`) and by Path B derived DSLs
* (e.g. dogfood `roomAndroidUtil`) so plugin identity stays on the type while the
* AGP/util wiring is shared. Call sites of derived DSLs remain attributes-only —
* never pass plugin ids here.
*/
fun Project.androidUtilTarget(
type: TargetType,
packageName: String,
owner: Owner = NoOwner,
visibility: Visibility = Public,
dependencies: FormaDependency = emptyDependency(),
testDependencies: FormaDependency = emptyDependency(),
/** Enable Jetpack Compose; defaults to project-wide `compose` setting. */
compose: Boolean = Forma.settings.compose,
) {
disallowResources()

AndroidTargetRegistry.selfValidator(type).asValidator().validate(target)
registerFormaLayout(packageName)

val androidFeatureConfig = AndroidLibraryFeatureConfiguration(
packageName = packageName,
compose = compose,
)

applyFeatures(
androidLibraryFeatureDefinition(androidFeatureConfig),
kotlinAndroidFeatureDefinition()
)
val processors = processorConfigurationFeatures()
applyTargetPlugins(type, configurationFeatures = processors)

applyDependencies(
validator = AndroidTargetRegistry.validatorFor(type).asValidator(),
dependencies = dependencies,
testDependencies = testDependencies,
configurationFeatures = processors
)
}
Loading