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
5 changes: 5 additions & 0 deletions .changeset/quality-lens-client-core.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
---
"tech-stack": minor
---

Add a required quality lens (AX and DX for backends, plus UX for frontends), a new reference for apps with child users (a separate child app, no third-party ads, analytics or crash SDKs, content-free push, minimal age data), and two Rust entries: a sans-I/O client core for clients that share protocol rules across platforms, and UniFFI for native bindings over a Kotlin Multiplatform bridge.
1 change: 1 addition & 0 deletions skills/tech-stack/SKILL.md
Original file line number Diff line number Diff line change
Expand Up @@ -24,6 +24,7 @@ Tech-stack owns stack, tooling, code layout and architecture choices; how agents
| Git hooks | `references/practices/hooks.md` |
| CI | `references/practices/ci.md` |
| Security scanning, containers, secrets | `references/practices/security.md` |
| An app with child users (stores, SDKs, push, age data) | `references/practices/child-users.md` |
| Database, migrations | `references/practices/data.md` |
| API or realtime contracts, generated clients | `references/practices/contracts.md` |
| Tests, coverage, complexity, baselines | `references/practices/testing.md` |
Expand Down
6 changes: 6 additions & 0 deletions skills/tech-stack/references/practices/architecture.md
Original file line number Diff line number Diff line change
@@ -1,5 +1,11 @@
# Architecture defaults

### Quality lens — required
setting: judge backend structure by AX and DX, and frontend structure by AX, DX and UX, where a structural change must not make the UI or UX worse
setting: AX: an agent works without reading everything, through fast deterministic checks whose failures arrive as text, predictable structure with much behaviour behind small interfaces, and lean always-loaded context; DX: a person can understand, navigate, review and contribute, and human review is part of DX
why: agents do most of the building and relearn the code every session, while people still navigate and review it; frontends also answer to their users
verified: 2026-10-23 · https://github.com/mattpocock/dictionary-of-ai-coding/blob/main/dictionary/AX.md · https://github.com/mattpocock/dictionary-of-ai-coding/blob/main/dictionary/DX.md

## Starting shape
- One modular monolith per product (over microservices) until a measured need splits it.
- PostgreSQL is the durable authority; durable jobs in the database (Oban for Elixir); realtime only wakes clients, it does not carry truth.
Expand Down
23 changes: 23 additions & 0 deletions skills/tech-stack/references/practices/child-users.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,23 @@
# Apps with child users

Store rules below are quoted from their sources; the rest are our defaults, stricter than the stores. Age thresholds and consent are legal questions: each project records its jurisdictions and their sources.

### Separate child app — default (over one app gated by account type)
setting: when the adult app carries social, commerce or other adult-only features; both apps share one backend and client core
why: our choice, not a store rule; Google's Families policy applies whenever "one of the target audiences for your app is children", and Apple 5.1.4 requires children's privacy compliance from any app able to share a minor's personal information, "the ability to chat" included; a separate app keeps adult features out of that scope and out of the child's reach
verified: 2026-10-23 · https://support.google.com/googleplay/android-developer/answer/9893335 · https://developer.apple.com/app-store/review/guidelines/

### Third-party ads, analytics and crash SDKs in a child app — rejected
setting: analytics first-party and server-side; crash data from App Store Connect and Play Console
why: our default; the stores allow some "in limited cases" (Apple 1.3) and Families requires approved or self-certified SDKs, so each one needs a review of what it sends about a child; none removes that review
verified: 2026-10-23 · https://developer.apple.com/app-store/review/guidelines/ · https://support.google.com/googleplay/android-developer/answer/9893335

### Content-free push to a child's device — default (over payloads that carry message text)
setting: a generic visible alert, then the app syncs over its own connection; the app works without push permission; on the web, show a generic notification for each push and never rely on silent push
why: Apple 4.5.4: push "should not be used to send sensitive personal or confidential information" and "must not be required for the app to function"; Chrome accepts only `userVisibleOnly: true` subscriptions; carrying no content at all is our stricter choice
verified: 2026-10-23 · https://developer.apple.com/app-store/review/guidelines/ · https://web.dev/articles/push-notifications-subscribing-a-user

### Birth month and year — default (over a full date of birth)
setting: collect only the age data the applicable rules need; keep child protections until a threshold is certainly reached (the month after the birth month); configure thresholds per jurisdiction and purpose, each with its source
why: Apple 5.1.4 allows a birthdate "only for the purpose of complying with these statutes"; less data held about a child, and thresholds that differ by country and feature stay configuration
verified: 2026-10-23 · https://developer.apple.com/app-store/review/guidelines/
10 changes: 10 additions & 0 deletions skills/tech-stack/references/stacks/rust.md
Original file line number Diff line number Diff line change
Expand Up @@ -27,3 +27,13 @@ verified: 2026-10-06 · https://github.com/bnjbvr/cargo-machete
setting: rebuild in CI, then `git diff --exit-code -- <output path>` (for example generated WASM)
why: a project's checked-in WASM could lag its Rust source with CI green
verified: 2026-10-06 · see `practices/ci.md`

### Sans-I/O client core — default (over a core that owns its network and storage)
setting: for clients that share protocol and state rules across platforms: one crate holds those rules; it takes commands and completions and emits effects, and per-platform host adapters do HTTP, sockets, storage, timers and randomness
why: each rule lives once, and shared protocol rules can be tested in memory without an emulator or network; host adapters need their own verification
verified: 2026-10-23 · https://sans-io.readthedocs.io/how-to-sans-io.html

### UniFFI for native bindings — default (over a Kotlin Multiplatform bridge)
setting: Swift and Kotlin call the generated bindings directly; our web build uses `wasm-bindgen`; expose an explicit cancel call, since UniFFI does not directly support cancelling async calls
why: Swift and Kotlin use UniFFI's maintained generators directly, without adding a third-party Kotlin Multiplatform generator and bridge
verified: 2026-10-23 · https://mozilla.github.io/uniffi-rs/latest/futures.html
Loading