Skip to content

Commit f8a2c1e

Browse files
Merge branch 'stage' into ADFA-2686-consistent-feedback-FAB
2 parents 100fdbc + 5f14ef7 commit f8a2c1e

35 files changed

Lines changed: 1964 additions & 69 deletions

File tree

Lines changed: 87 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,87 @@
1+
---
2+
name: architecture-review
3+
description: Review a code change against Code On The Go's architecture — ARCHITECTURE.md and the ADRs in docs/adr/. Forces a read of those authoritative docs (they are NOT reliably in context otherwise), then checks the diff against UDF/state, Koin DI, Room-vs-SQLite, Compose, module boundaries, ABI flavors, dependency substitution, @Parcelize, and strings placement, reporting violations with the ADR/section each comes from. Use when asked to review architecture alignment, check a change against ARCHITECTURE.md/the ADRs, or as the §10 step of a code review.
4+
metadata:
5+
author: Hal Eisen
6+
keywords:
7+
- architecture
8+
- review
9+
- adr
10+
- udf
11+
- code-review
12+
- codeonthego
13+
---
14+
15+
## Why this is a skill (not just REVIEW.md §10)
16+
17+
ARCHITECTURE.md (~4.9k tokens) and the ADRs (~7.3k) are **not** in context during normal work, and prose links in REVIEW.md are not reliably followed. This skill exists to **guarantee the read**: it opens the authoritative docs, then checks the diff against them. Do not review architecture from memory or from the summary in this file alone.
18+
19+
## When to invoke
20+
21+
- "Review this against the architecture / ARCHITECTURE.md / the ADRs."
22+
- "Does this change follow our patterns?" / architecture-alignment review.
23+
- As the §10 step of a full code review — its output feeds REVIEW.md's evidence ledger.
24+
25+
## Step 1 — Scope the diff
26+
27+
Pick the target and get the changed files + hunks:
28+
- Working tree: `git diff --stat` and `git diff`.
29+
- This branch vs the integration branch: `git diff origin/stage...HEAD` (feature branches are based on `stage`).
30+
- A GitHub PR: `gh pr diff <N>`.
31+
32+
**Exclude vendored/generated code** — it's not held to our patterns: `composite-builds/build-deps*`, `subprojects/{aaptcompiler,builder-model-impl,flashbar,xml-dom}`, `termux/`, `eventbus/`, `LayoutEditor/`, `**/build/`, generated `R`/`BuildConfig`. Review only first-party Kotlin/Java/XML/Gradle changes.
33+
34+
## Step 2 — READ the authoritative docs (mandatory)
35+
36+
Before judging anything, read:
37+
- `ARCHITECTURE.md` (whole file — module map, dependency rules, tech stack, **State Management**, testing).
38+
- **Every** file in `docs/adr/*.md`. At minimum open the ones a diff can plausibly violate: 0001 (Room), 0003 (substitution), 0005 (flavors), 0006 (Koin), 0009 (Compose). Read the rest if the change is broad.
39+
40+
Do not skip this because the rules "look familiar" — they are the source of truth and they change.
41+
42+
## Step 3 — Check the diff against the rules
43+
44+
For each changed first-party file, check the applicable rules. Each rule cites its source so findings are traceable.
45+
46+
| # | Rule | Source |
47+
|---|---|---|
48+
| 1 | **UDF:** new screens use `ViewModel` + `StateFlow<UiState>`, sealed `UiEvent`/`UiEffect`, a repository for data; composables collect via `collectAsStateWithLifecycle()` (lifecycle-aware, Android's strongly-recommended default; `collectAsState()` is only for platform-agnostic/KMP code, which we don't have); no I/O or business logic in composables/Activities/Fragments. | ARCHITECTURE.md → State Management |
49+
| 2 | **Sealed state for mutually-exclusive states** (loading/content/error/…): not a `data class` of independent `Boolean`s that can contradict each other ("boolean hell"). | ARCHITECTURE.md → State Management |
50+
| 3 | **Koin DI**, constructor injection; register new singletons/ViewModels in the module. No hand-rolled singletons/service locators (the documented `ServiceLocator` aside). | ADR 0006 |
51+
| 4 | **Persistence:** Room is the default for relational data; raw SQLite only for a justified exception (prebuilt read-only DB, perf/allocation-critical indexing, cross-boundary schema) — and the PR must say which. Non-relational → filesystem/preferences (DataStore). | ADR 0001 |
52+
| 5 | **`@Parcelize`** (`kotlin-parcelize`) for `Parcelable`; never hand-implement it unless Parcelize genuinely can't. | ARCHITECTURE.md → Parceling |
53+
| 6 | **New UI is Jetpack Compose** — a new XML-layout / `Fragment`-rendered screen for the IDE's own UI is a violation (existing XML screens are fine until reworked). | ADR 0009 |
54+
| 7 | **Module boundaries / dependency direction:** UI → ViewModel → Repository → data source; features depend on `common`/`utils`, not the reverse; no new cross-feature or upward dependency. | ARCHITECTURE.md → module map |
55+
| 8 | **ABI flavors:** new Android modules get `v7`/`v8` via `composite-builds/build-logic` centrally — no per-module flavor blocks, no flavorless `assembleDebug`. (`:plugin-api` is intentionally flavorless.) | ADR 0005 |
56+
| 9 | **Dependency substitution:** don't add a Maven coordinate for something already vendored/substituted (`build-deps*`); don't add a new dependency without checking `gradle/libs.versions.toml` first. | ADR 0003 |
57+
| 10 | **Strings** live in the `:resources` module's `strings.xml` (not per-module, not inline literals). | REVIEW.md §7 |
58+
| 11 | **UI never drawn over the two system bars** (top status bar, bottom navigation bar). | CLAUDE.md |
59+
60+
Rules 1, 2, 6 apply to UI changes; 4, 5 to data/model changes; 8, 9 to Gradle changes. Judge by what the diff touches — don't flag rules a file doesn't engage.
61+
62+
For a **large diff (~15+ first-party files)**, fan out: spawn a subagent per dimension (UI/state, DI, persistence, Gradle/modules), each instructed to read the relevant ADR and report only its dimension's findings; then merge. For a small diff, do it inline.
63+
64+
## Step 4 — Report
65+
66+
Output a findings table, most-severe first. Every finding must cite the rule source and give a concrete fix. If a rule was checked and passes, say so (the evidence ledger wants pass/fail, not silence).
67+
68+
```
69+
### Architecture review — <scope>
70+
Docs read: ARCHITECTURE.md, docs/adr/0001,0003,0005,0006,0009 (+others as needed)
71+
72+
| Verdict | File:line | Rule | Source | Fix |
73+
|---|---|---|---|---|
74+
| ❌ | ui/FooScreen.kt:42 | Mutually-exclusive state as booleans | ADR 0001 sibling / State Mgmt | Model as a sealed FooUiState (Loading/Content/Error) |
75+
| ⚠️ | data/BarStore.kt:10 | Raw SQLite without stated justification | ADR 0001 | Use Room, or state which exception applies in the PR |
76+
| ✅ | — | Koin DI | ADR 0006 | new VM registered in module, constructor-injected |
77+
78+
Summary: <n> violations, <n> warnings, <n> checks passed.
79+
```
80+
81+
- **❌ violation** = contradicts a rule; blocking. **⚠️ warning** = likely issue / missing justification. **✅** = checked and clean.
82+
- If nothing architectural changed (e.g. a docs- or test-only diff), say so explicitly rather than inventing findings.
83+
84+
## Notes
85+
86+
- This is a *conformance* check against our documented patterns — not a general bug hunt (use `/code-review` for correctness). Keep findings tied to a specific ADR/section.
87+
- If a rule seems wrong or outdated for the change at hand, flag it as a possible ADR update rather than forcing the code to fit — the ADRs are `Proposed`, not immutable.
Lines changed: 51 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,51 @@
1+
#!/bin/bash
2+
3+
# Non-blocking nudge: when a push includes changes to first-party app source,
4+
# remind the author to run an architecture pass (the architecture-review skill /
5+
# REVIEW.md §10) before opening the PR.
6+
#
7+
# This is a REMINDER, not a gate — it always exits 0 and never blocks a push.
8+
# It stays silent unless the push actually touches first-party Kotlin/Java, so
9+
# docs-, test-, and vendored-only pushes produce no output.
10+
11+
set -u
12+
13+
cyan=$(tput setaf 6 2>/dev/null || true)
14+
yellow=$(tput setaf 3 2>/dev/null || true)
15+
reset=$(tput sgr0 2>/dev/null || true)
16+
17+
# Determine the commits being pushed. Prefer the tracked upstream; fall back to
18+
# the integration branch (feature branches are based on stage). If neither is
19+
# resolvable, stay quiet rather than nag.
20+
if git rev-parse --abbrev-ref --symbolic-full-name '@{upstream}' >/dev/null 2>&1; then
21+
range="@{upstream}..HEAD"
22+
elif git rev-parse --verify -q origin/stage >/dev/null 2>&1; then
23+
range="origin/stage..HEAD"
24+
else
25+
exit 0
26+
fi
27+
28+
changed=$(git diff --name-only "$range" 2>/dev/null) || exit 0
29+
[ -n "$changed" ] || exit 0
30+
31+
# Keep only first-party production Kotlin/Java: drop tests, generated build
32+
# output, and vendored subtrees (mirrors Spotless's commonTargetExcludes).
33+
firstparty=$(printf '%s\n' "$changed" \
34+
| grep -E '\.(kt|java)$' \
35+
| grep -vE '(^|/)(build|src/test|src/androidTest)/' \
36+
| grep -vE '^(composite-builds/build-deps|termux/|eventbus/|LayoutEditor/|subprojects/(aaptcompiler|builder-model-impl|flashbar|xml-dom|llama\.cpp)/)' \
37+
|| true)
38+
39+
[ -n "$firstparty" ] || exit 0
40+
41+
count=$(printf '%s\n' "$firstparty" | grep -c .)
42+
43+
echo ""
44+
echo "${cyan}[architecture nudge]${reset} this push changes ${count} first-party source file(s)."
45+
echo "${yellow} Consider an architecture pass before opening the PR:${reset}"
46+
echo " - run the ${cyan}architecture-review${reset} skill (reads ARCHITECTURE.md + the ADRs, checks the diff), or"
47+
echo " - self-check against ${cyan}REVIEW.md section 10${reset} (UDF, sealed state, Koin, Room, module boundaries, Compose)."
48+
echo "${yellow} (Reminder only — your push continues.)${reset}"
49+
echo ""
50+
51+
exit 0

‎.github/workflows/analyze.yml‎

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -79,7 +79,7 @@ jobs:
7979
# Firebase config). Scoped to this step so SonarCloud and other
8080
# third-party actions never see them.
8181
FIREBASE_CONSOLE_URL: ${{ secrets.FIREBASE_CONSOLE_URL }}
82-
SENTRY_DSN_DEBUG: ${{ secrets.SENTRY_DSN_DEBUG }}
82+
GLITCHTIP_DSN: ${{ secrets.GLITCHTIP_DSN }}
8383
run: |
8484
echo "gradle_time_start=$(date +%s)" >> $GITHUB_ENV
8585
flox activate -d flox/base -- ./gradlew :app:assembleV8Debug --no-daemon
@@ -104,7 +104,7 @@ jobs:
104104
# The Gradle build also drives Sentry/Firebase configuration during
105105
# the unit-test compile path.
106106
FIREBASE_CONSOLE_URL: ${{ secrets.FIREBASE_CONSOLE_URL }}
107-
SENTRY_DSN_DEBUG: ${{ secrets.SENTRY_DSN_DEBUG }}
107+
GLITCHTIP_DSN: ${{ secrets.GLITCHTIP_DSN }}
108108
run: flox activate -d flox/base -- ./gradlew :testing:tooling:assemble :testing:common:assemble sonarqube --info --no-build-cache -x lint --continue
109109

110110
- name: Upload JaCoCo report

‎.github/workflows/debug.yml‎

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -28,7 +28,7 @@ env:
2828
ORG_GRADLE_PROJECT_signingInMemoryKeyId: ${{ secrets.MVN_SIGNING_KEY_ID }}
2929
ORG_GRADLE_PROJECT_signingInMemoryKeyPassword: ${{ secrets.MVN_SIGNING_KEY_PASSWORD }}
3030
FIREBASE_CONSOLE_URL: ${{ secrets.FIREBASE_CONSOLE_URL }}
31-
SENTRY_DSN_DEBUG: ${{ secrets.SENTRY_DSN_DEBUG }}
31+
GLITCHTIP_DSN: ${{ secrets.GLITCHTIP_DSN }}
3232

3333
jobs:
3434
check_changes:

‎.github/workflows/release.yml‎

Lines changed: 1 addition & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -25,10 +25,7 @@ env:
2525
ORG_GRADLE_PROJECT_signingInMemoryKeyId: ${{ secrets.MVN_SIGNING_KEY_ID }}
2626
ORG_GRADLE_PROJECT_signingInMemoryKeyPassword: ${{ secrets.MVN_SIGNING_KEY_PASSWORD }}
2727
FIREBASE_CONSOLE_URL: ${{ secrets.FIREBASE_CONSOLE_URL }}
28-
SENTRY_DSN_RELEASE: ${{ secrets.SENTRY_DSN_RELEASE }}
29-
SENTRY_ORG: ${{ secrets.SENTRY_ORG }}
30-
SENTRY_PROJECT: ${{ secrets.SENTRY_PROJECT }}
31-
SENTRY_AUTH_TOKEN: ${{ secrets.SENTRY_AUTH_TOKEN }}
28+
GLITCHTIP_DSN: ${{ secrets.GLITCHTIP_DSN }}
3229

3330
jobs:
3431
merge_stage_to_main:

‎AGENTS.md‎

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,5 @@
1+
# AGENTS.md
2+
3+
The operational rules for AI agents and contributors live in **[CLAUDE.md](CLAUDE.md)** — build/test commands, ABI flavors, emulator/device selection, project constraints, and the CI/Jira/SonarQube/git-messaging conventions.
4+
5+
This file is a pointer so agents that follow the `AGENTS.md` convention find the guidance; the content is maintained in one place (CLAUDE.md) to avoid drift.

0 commit comments

Comments
 (0)