From 107cd3d9e447bd6c203f28911416bc8357546b9b Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Olu=CC=81was=CC=A3e=CC=80yi=CC=81=20S=CC=A3o=CC=81bande?= =?UTF-8?q?=CC=81?= Date: Fri, 23 Oct 2026 13:48:23 +0100 Subject: [PATCH 1/3] docs: add the quality lens, child-user app defaults and a shared client core Backends serve AX and DX; frontends also serve UX without making UI or UX worse. Apps with child users get their own app, no third-party SDKs, content-free push and age stored as birth month and year. Rust gains a sans-I/O client core entry and UniFFI over a Kotlin Multiplatform bridge. --- .changeset/quality-lens-client-core.md | 5 +++++ skills/tech-stack/SKILL.md | 1 + .../tech-stack/references/practices/architecture.md | 10 ++++++++++ skills/tech-stack/references/stacks/rust.md | 11 +++++++++++ 4 files changed, 27 insertions(+) create mode 100644 .changeset/quality-lens-client-core.md diff --git a/.changeset/quality-lens-client-core.md b/.changeset/quality-lens-client-core.md new file mode 100644 index 0000000..0b7d1b6 --- /dev/null +++ b/.changeset/quality-lens-client-core.md @@ -0,0 +1,5 @@ +--- +"tech-stack": minor +--- + +Add a quality lens for backends and frontends (AX, DX and UX), defaults for apps with child users (a separate child app, no third-party SDKs, content-free push, age as birth month and year), and two Rust entries: a sans-I/O client core and UniFFI for native bindings over a Kotlin Multiplatform bridge. diff --git a/skills/tech-stack/SKILL.md b/skills/tech-stack/SKILL.md index 7c5979f..330d080 100644 --- a/skills/tech-stack/SKILL.md +++ b/skills/tech-stack/SKILL.md @@ -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/architecture.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` | diff --git a/skills/tech-stack/references/practices/architecture.md b/skills/tech-stack/references/practices/architecture.md index aa4c2c5..bd337bd 100644 --- a/skills/tech-stack/references/practices/architecture.md +++ b/skills/tech-stack/references/practices/architecture.md @@ -1,5 +1,9 @@ # Architecture defaults +## Quality lens — required +- Backends serve AX and DX. AX: an agent works without reading everything (predictable structure, much behaviour behind small interfaces, names that say what things do) and corrects itself from fast, deterministic checks whose failures arrive as text. DX: a person can understand, navigate, contribute and review; a diff stays inside one module or language. Terms as in the [AI Coding Dictionary](https://github.com/mattpocock/dictionary-of-ai-coding). +- Frontends serve AX, DX and UX: a structural change must not make the UI or UX worse. + ## 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. @@ -17,6 +21,12 @@ - Take the client IP from a trusted-proxy plug before a rate limit or audit uses it. - Write another module's rows through its owner function, inside the caller's transaction. +## Apps with child users +- Give child users their own app when the adult app may carry social or commerce features: Google's Families policy covers "any apps that have at least one target audience age group that includes children", and Apple 5.1.4 applies children's privacy law to any app that lets a minor chat; verified: 2026-10-23 · [Families](https://support.google.com/googleplay/android-developer/answer/9867159), [App Review](https://developer.apple.com/app-store/review/guidelines/). +- Link no third-party ads, analytics or crash SDKs into an app with child users; keep analytics first-party and server-side, and take crash data from the platform consoles. +- Push carries no content: a visible generic alert, then the app syncs. Web push must show a notification for every push, and Apple 4.5.4 bars sensitive information in push. +- Store a child's age as birth month and year; keep each country's age thresholds (12, 13, 16, 18) as configuration. + ## Records every repo keeps - Agent instruction files follow [the agent instructions default](agents.md#agent-instructions--default-over-a-claudemd-import-with-claude-only-lines). - Non-goals in a README "Non-goals" section or an existing `.nongoals` file. diff --git a/skills/tech-stack/references/stacks/rust.md b/skills/tech-stack/references/stacks/rust.md index 4e6b4d0..74ae1ce 100644 --- a/skills/tech-stack/references/stacks/rust.md +++ b/skills/tech-stack/references/stacks/rust.md @@ -27,3 +27,14 @@ verified: 2026-10-06 · https://github.com/bnjbvr/cargo-machete setting: rebuild in CI, then `git diff --exit-code -- ` (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: one crate holds the client protocol rules (API calls, session, sync and repair, local state, cryptography); it takes commands and completions and emits effects; host adapters per platform do HTTP, sockets, storage, timers, randomness and key sealing +setting: the storage port is one atomic compare-and-set save with leases, not key-value; keys stay behind opaque seal and open handles; one repair owner, with sockets and push only waking it +why: each rule lives once for web, iOS and Android, and tests need no emulator or network +verified: 2026-10-23 · https://sans-io.readthedocs.io/ + +### UniFFI for native bindings — default (over a Kotlin Multiplatform bridge) +setting: Swift and Kotlin call the generated bindings directly; the web uses `wasm-bindgen`; expose an explicit cancel call, since async maps to Kotlin `suspend` and Swift `async` without built-in cancellation +why: UniFFI's built-in bindings are Kotlin, Swift and Python; Kotlin Multiplatform generators are third-party and pre-1.0 +verified: 2026-10-23 · https://mozilla.github.io/uniffi-rs/latest/ From 323eaa0831b37fc4b51fb5d4ec4856ac7e7d6bb8 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Olu=CC=81was=CC=A3e=CC=80yi=CC=81=20S=CC=A3o=CC=81bande?= =?UTF-8?q?=CC=81?= Date: Fri, 23 Oct 2026 14:35:48 +0100 Subject: [PATCH 2/3] docs: correct sources and narrow defaults after review Move child-user defaults into their own reference as standard entries, separate store rules from our stricter choices, scope the sans-I/O core to clients that share protocol rules, and correct the reason for preferring UniFFI's own generators. --- .changeset/quality-lens-client-core.md | 2 +- skills/tech-stack/SKILL.md | 2 +- .../references/practices/architecture.md | 11 ++------- .../references/practices/child-users.md | 23 +++++++++++++++++++ skills/tech-stack/references/stacks/rust.md | 13 +++++------ 5 files changed, 33 insertions(+), 18 deletions(-) create mode 100644 skills/tech-stack/references/practices/child-users.md diff --git a/.changeset/quality-lens-client-core.md b/.changeset/quality-lens-client-core.md index 0b7d1b6..cc3be17 100644 --- a/.changeset/quality-lens-client-core.md +++ b/.changeset/quality-lens-client-core.md @@ -2,4 +2,4 @@ "tech-stack": minor --- -Add a quality lens for backends and frontends (AX, DX and UX), defaults for apps with child users (a separate child app, no third-party SDKs, content-free push, age as birth month and year), and two Rust entries: a sans-I/O client core and UniFFI for native bindings over a Kotlin Multiplatform bridge. +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. diff --git a/skills/tech-stack/SKILL.md b/skills/tech-stack/SKILL.md index 330d080..162dec5 100644 --- a/skills/tech-stack/SKILL.md +++ b/skills/tech-stack/SKILL.md @@ -24,7 +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/architecture.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` | diff --git a/skills/tech-stack/references/practices/architecture.md b/skills/tech-stack/references/practices/architecture.md index bd337bd..90c756b 100644 --- a/skills/tech-stack/references/practices/architecture.md +++ b/skills/tech-stack/references/practices/architecture.md @@ -1,8 +1,7 @@ # Architecture defaults -## Quality lens — required -- Backends serve AX and DX. AX: an agent works without reading everything (predictable structure, much behaviour behind small interfaces, names that say what things do) and corrects itself from fast, deterministic checks whose failures arrive as text. DX: a person can understand, navigate, contribute and review; a diff stays inside one module or language. Terms as in the [AI Coding Dictionary](https://github.com/mattpocock/dictionary-of-ai-coding). -- Frontends serve AX, DX and UX: a structural change must not make the UI or UX worse. +## Quality lens +- Quality lens — required: 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. [AX](https://github.com/mattpocock/dictionary-of-ai-coding/blob/main/dictionary/AX.md): 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](https://github.com/mattpocock/dictionary-of-ai-coding/blob/main/dictionary/DX.md): a person can understand, navigate, review and contribute; human review is part of DX. Verified: 2026-10-23. ## Starting shape - One modular monolith per product (over microservices) until a measured need splits it. @@ -21,12 +20,6 @@ - Take the client IP from a trusted-proxy plug before a rate limit or audit uses it. - Write another module's rows through its owner function, inside the caller's transaction. -## Apps with child users -- Give child users their own app when the adult app may carry social or commerce features: Google's Families policy covers "any apps that have at least one target audience age group that includes children", and Apple 5.1.4 applies children's privacy law to any app that lets a minor chat; verified: 2026-10-23 · [Families](https://support.google.com/googleplay/android-developer/answer/9867159), [App Review](https://developer.apple.com/app-store/review/guidelines/). -- Link no third-party ads, analytics or crash SDKs into an app with child users; keep analytics first-party and server-side, and take crash data from the platform consoles. -- Push carries no content: a visible generic alert, then the app syncs. Web push must show a notification for every push, and Apple 4.5.4 bars sensitive information in push. -- Store a child's age as birth month and year; keep each country's age thresholds (12, 13, 16, 18) as configuration. - ## Records every repo keeps - Agent instruction files follow [the agent instructions default](agents.md#agent-instructions--default-over-a-claudemd-import-with-claude-only-lines). - Non-goals in a README "Non-goals" section or an existing `.nongoals` file. diff --git a/skills/tech-stack/references/practices/child-users.md b/skills/tech-stack/references/practices/child-users.md new file mode 100644 index 0000000..daaa2df --- /dev/null +++ b/skills/tech-stack/references/practices/child-users.md @@ -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/ diff --git a/skills/tech-stack/references/stacks/rust.md b/skills/tech-stack/references/stacks/rust.md index 74ae1ce..b255934 100644 --- a/skills/tech-stack/references/stacks/rust.md +++ b/skills/tech-stack/references/stacks/rust.md @@ -29,12 +29,11 @@ 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: one crate holds the client protocol rules (API calls, session, sync and repair, local state, cryptography); it takes commands and completions and emits effects; host adapters per platform do HTTP, sockets, storage, timers, randomness and key sealing -setting: the storage port is one atomic compare-and-set save with leases, not key-value; keys stay behind opaque seal and open handles; one repair owner, with sockets and push only waking it -why: each rule lives once for web, iOS and Android, and tests need no emulator or network -verified: 2026-10-23 · https://sans-io.readthedocs.io/ +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; the web uses `wasm-bindgen`; expose an explicit cancel call, since async maps to Kotlin `suspend` and Swift `async` without built-in cancellation -why: UniFFI's built-in bindings are Kotlin, Swift and Python; Kotlin Multiplatform generators are third-party and pre-1.0 -verified: 2026-10-23 · https://mozilla.github.io/uniffi-rs/latest/ +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 From 2dcd39483096ffb7f2d454c9b6510d2f8b41533a Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Olu=CC=81was=CC=A3e=CC=80yi=CC=81=20S=CC=A3o=CC=81bande?= =?UTF-8?q?=CC=81?= Date: Fri, 23 Oct 2026 14:42:05 +0100 Subject: [PATCH 3/3] docs: write the quality lens as a standard entry --- skills/tech-stack/references/practices/architecture.md | 7 +++++-- 1 file changed, 5 insertions(+), 2 deletions(-) diff --git a/skills/tech-stack/references/practices/architecture.md b/skills/tech-stack/references/practices/architecture.md index 90c756b..cfe3783 100644 --- a/skills/tech-stack/references/practices/architecture.md +++ b/skills/tech-stack/references/practices/architecture.md @@ -1,7 +1,10 @@ # Architecture defaults -## Quality lens -- Quality lens — required: 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. [AX](https://github.com/mattpocock/dictionary-of-ai-coding/blob/main/dictionary/AX.md): 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](https://github.com/mattpocock/dictionary-of-ai-coding/blob/main/dictionary/DX.md): a person can understand, navigate, review and contribute; human review is part of DX. Verified: 2026-10-23. +### 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.