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
2 changes: 1 addition & 1 deletion TICKETS.md
Original file line number Diff line number Diff line change
Expand Up @@ -176,7 +176,7 @@ Open coding: **F-115** (`in_progress`, **`priority: now`**, **cron may continue*

| ID | Status | Title | Notes |
|----|--------|-------|-------|
| F-115 | in_progress · **priority: now** · **cron may continue** | 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 through WorkManager sync done:** Hilt Path A + Room Path B + Firebase Path A + Preferences DataStore + ForYou/Bookmarks/Search/Settings + **`sync-work-android-util`** (WorkManager + Hilt Work library stack; binary `Sync.initialize`). Findings F16–F24 (6-feature root + sync leaf). **Next phase C:** Proto DataStore / flavors. No `androidLibrary`. |
| F-115 | in_progress · **priority: now** · **cron may continue** | 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 through Proto DataStore done:** Hilt Path A + Room Path B + Firebase Path A + Preferences→**Proto** DataStore + ForYou/Bookmarks/Search/Settings + WorkManager sync + **`core-datastore-proto-library`** (`protobufLibrary` Path B). Findings F16–F26. **Next phase C:** flavors / full designsystem. No `androidLibrary`. |

## Backlog (lower priority / historical GitHub)

Expand Down
3 changes: 2 additions & 1 deletion docs/CALL-SITE-SURFACE.md
Original file line number Diff line number Diff line change
Expand Up @@ -47,7 +47,8 @@ All return **`Unit`**.

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

## JVM target DSLs (`tools.forma.jvm`)
Expand Down
24 changes: 21 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 + Room + Firebase + DataStore + features + Settings + **WorkManager sync green** (Proto DataStore / flavors next)
**Status:** `in_progress` — phase C Hilt + Room + Firebase + **Proto DataStore** + features + Settings + WorkManager green (flavors / full designsystem 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 `androidUtilTarget` + prior KSP fix)
**Forma plugins:** `mavenLocal` version **`0.1.3-NIA`** (republish after `libraryTarget` + `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 @@ -323,7 +323,23 @@ Workspace: same **`forma-spike`** external tree.
| **F24** | `deps()` cannot mix `.ksp` NamedDependency + `target()` in one overload — compose with `+` |
| Verify | `forma-spike` `./gradlew :binary:assembleDebug` → **BUILD SUCCESSFUL** (486 tasks); APK ~13 MB |

**Still open in phase C:** flavors, full designsystem, Proto DataStore parity.
**Still open in phase C (pre-Proto DataStore):** flavors, full designsystem, Proto DataStore parity.

### Phase C — Proto DataStore ✅ (2026-07-31)

| Step | Result |
|------|--------|
| Engine | `libraryTarget(type, …)` — Path B twin of `androidUtilTarget`/`resourcesTarget`; `library` delegates |
| `forma-defs` | `ProtobufLibraryType` = `deriveTargetType(base=jvmLibrary, suffix=library)` + thin **`protobufLibrary`** |
| Module | `core-datastore-proto-library` — slim `UserPreferences` + theme enums (lite Java/Kotlin) |
| DataStore | `core-datastore-android-util` — typed `DataStore<UserPreferences>` + `UserPreferencesSerializer`; no Preferences API |
| Companions | `transitiveDeps(androidx.datastore:datastore + protobuf-kotlin-lite)` — DataStore still **no** structure plugin (F18); protobuf lite transitive for consumers (F17 sibling / **F26**) |
| Classpath | `extraPlugins` += protobuf Gradle plugin; settings `plugin(protobuf-gradle-plugin)` |
| **F25** | Path B protobuf on JVM `library` — type owns `com.google.protobuf`; call site attrs-only |
| **F26** | Generated proto types expose lite supers → consumers need `protobuf-kotlin-lite` on compile classpath |
| Verify | `forma-spike` `./gradlew :binary:assembleDebug` → **BUILD SUCCESSFUL** (493 tasks); APK ~14 MB |

**Still open in phase C:** flavors, full designsystem port.

### Phase D — Case study write-up

Expand Down Expand Up @@ -356,6 +372,8 @@ Workspace: same **`forma-spike`** external tree.
| F22 | 2026-07-31 | Settings upstream impl-only | NiA `:feature:settings:impl` has no api module. Spike adds `settings-api` (SettingsNavKey only) so root/other features navigate via **api** ports (F1) without depending on settings impl. Theme prefs on Preferences DataStore — still no Proto. |
| F23 | 2026-07-31 | sync work notification strings | `androidUtil` content rule = no `res/`. Spike hardcodes notification title/channel (system sync icon). Optional later: `sync-work-res` `androidRes` + matrix edge. |
| F24 | 2026-07-31 | `deps()` overload mix | Cannot pass `.ksp` NamedDependency and `target()` FormaTarget in one `deps(...)` call (distinct overloads). Compose `transitiveDeps + deps(ksp) + deps(target)`. |
| F25 | 2026-07-31 | Protobuf needs type-owned apply on JVM library | NiA `datastore-proto` is pure JVM + `com.google.protobuf`. Engine needed `libraryTarget` (like `androidUtilTarget`) so Path B `protobufLibrary` can pass derived type. Call sites stay attrs-only. |
| F26 | 2026-07-31 | Proto lite supers on consumers | `UserPreferences` / enums extend protobuf lite; androidUtil consumer must `transitiveDeps(protobuf-kotlin-lite)` even when proto module already depends on it (project dep does not re-export non-api runtime the same way as NiA `api(libs.protobuf…)`). |

## Local reference commands

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

Newest entries first.

## 2026-07-31 — F-115: NiA dogfood Proto DataStore

- **Ticket:** F-115 still `in_progress` · `priority: now` · `cron may continue` (flavors / full designsystem next)
- **Skills/modes:** Hermes direct dogfood (external spike + mechanical `libraryTarget` engine helper; no Grok Build this slice)
- **Engine (forma repo):**
- `libraryTarget(type, …)` — Path B twin of `androidUtilTarget`/`resourcesTarget`; `library` delegates
- **External tree:** `/Users/claw/work/nowinandroid-forma/forma-spike`
- `forma-defs/ProtobufLibrary.kt` — Path B `deriveTargetType(base=jvmLibrary)` + thin `protobufLibrary`
- Module: `core-datastore-proto-library` — slim UserPreferences + theme protos (lite)
- DataStore: typed `DataStore<UserPreferences>` + serializer; drop Preferences API
- Classpath: protobuf Gradle plugin on settings/`extraPlugins`
- **F25:** protobuf type-owned on JVM library via Path B
- **F26:** consumers need `transitiveDeps(protobuf-kotlin-lite)` for lite supers
- **Verify (real host):**
- `bash scripts/publish-local.sh 0.1.3-NIA` → ok
- `forma-spike` `./gradlew :binary:assembleDebug` → **BUILD SUCCESSFUL** (493 tasks)
- APK `binary-debug.apk` ~14 MB
- **Docs:** `docs/DOGFOOD-NIA.md` phase C Proto DataStore + F25/F26; TICKETS F-115 notes; CALL-SITE / TARGET-PLUGINS helpers; spike README
- **Commits/PRs:** this branch → PR base `v2`
- **Blockers:** none product; F-094 Portal still human-blocked
- **Next:** flavors and/or full designsystem port

## 2026-07-31 — F-115: NiA dogfood WorkManager sync

- **Ticket:** F-115 still `in_progress` · `priority: now` · `cron may continue` (Proto DataStore / flavors next)
Expand Down
4 changes: 3 additions & 1 deletion docs/TARGET-PLUGINS.md
Original file line number Diff line number Diff line change
Expand Up @@ -33,7 +33,9 @@ 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).
(same role as `resourcesTarget` for res).
**Dogfood Path B JVM library:** NiA spike `protobufLibrary` via engine helper `libraryTarget`
— 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
28 changes: 7 additions & 21 deletions plugins/android/src/main/java/library.kt
Original file line number Diff line number Diff line change
@@ -1,22 +1,15 @@
import tools.forma.android.feature.applyFeatures
import tools.forma.android.feature.kotlinFeatureDefinition
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.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

/**
* Can't depend on api\impl
*
* Path B derived JVM library kinds (Protobuf, etc.) use [libraryTarget] with a derived type.
*/
fun Project.library(
packageName: String,
Expand All @@ -25,19 +18,12 @@ fun Project.library(
visibility: Visibility = Public,
testDependencies: FormaDependency = emptyDependency()
) {
AndroidTargetRegistry.selfValidator(AndroidTargetTypes.jvmLibrary).asValidator().validate(target)
registerFormaLayout(packageName)

applyFeatures(
kotlinFeatureDefinition()
)
val processors = processorConfigurationFeatures()
applyTargetPlugins(AndroidTargetTypes.jvmLibrary, configurationFeatures = processors)

applyDependencies(
validator = AndroidTargetRegistry.validatorFor(AndroidTargetTypes.jvmLibrary).asValidator(),
libraryTarget(
type = AndroidTargetTypes.jvmLibrary,
packageName = packageName,
dependencies = dependencies,
owner = owner,
visibility = visibility,
testDependencies = testDependencies,
configurationFeatures = processors
)
}
49 changes: 49 additions & 0 deletions plugins/android/src/main/java/libraryTarget.kt
Original file line number Diff line number Diff line change
@@ -0,0 +1,49 @@
import tools.forma.android.feature.applyFeatures
import tools.forma.android.feature.kotlinFeatureDefinition
import tools.forma.android.feature.processorConfigurationFeatures
import tools.forma.android.target.AndroidTargetRegistry
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.FormaDependency
import tools.forma.deps.core.applyDependencies
import tools.forma.deps.core.applyTargetPlugins
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

/**
* Pure JVM library target bound to an arbitrary [TargetType].
*
* Used by [library] (pre-defined `jvm.library`) and by Path B derived DSLs
* (e.g. dogfood `protobufLibrary`) so plugin identity stays on the type while the
* Kotlin/JVM wiring is shared. Call sites of derived DSLs remain attributes-only —
* never pass plugin ids here.
*/
fun Project.libraryTarget(
type: TargetType,
packageName: String,
dependencies: FormaDependency = emptyDependency(),
owner: Owner = NoOwner,
visibility: Visibility = Public,
testDependencies: FormaDependency = emptyDependency(),
) {
AndroidTargetRegistry.selfValidator(type).asValidator().validate(target)
registerFormaLayout(packageName)

applyFeatures(
kotlinFeatureDefinition()
)
val processors = processorConfigurationFeatures()
applyTargetPlugins(type, configurationFeatures = processors)

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