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
1 change: 1 addition & 0 deletions skills.sh.json
Original file line number Diff line number Diff line change
Expand Up @@ -44,6 +44,7 @@
"kernel-audit-security",
"kernel-audit-monorepo",
"kernel-audit-review",
"chase-review",
"kernel-audit-swiftui",
"kernel-audit-trace"
]
Expand Down
78 changes: 78 additions & 0 deletions skills/chase-review/SKILL.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,78 @@
---
name: chase-review
Comment thread
charlesponti marked this conversation as resolved.
description: "Charles's engineering, product, design and workflow preferences in one place. Use to review a file, diff or PR, to refactor a repo toward his taste, and as the checklist before writing code, a PRD, a screen, a commit or a PR in any of his repos."
license: MIT
compatibility: Any repo. TypeScript, React Native and Swift are covered in depth; the principles carry to other stacks.
metadata:
author: charlesponti
version: "3.0"
category: Engineering
---

# chase-review

Charles's preferences, gathered from how he reviews, corrects and steers work across Thames,
Hominem and his other repos. Most are inferences from his requests, not a style guide he wrote,
so each reference marks where a rule is his exact word (dated) and where it is inferred.

## Precedence

1. What Charles says in the moment wins. Then update the matching reference file right away.
2. A repo's own written rules (`AGENTS.md`, `CLAUDE.md`, a repo-local skill) beat this skill where
they conflict. They usually add to it.
3. This skill fills every gap the repo leaves open.
4. Charles is the product manager and architect. Never invent product behavior, navigation or
architecture and present it as decided (`references/workflow.md`, "Decision authority").

## How to run a review

1. Read the file, diff or PR in full before judging it.
2. Find the repo's own checks (type check, lint, format, tests) from its docs or manifests. Use those.
3. Open only the references the change touches (table below). Check each rule.
4. Report only real violations: file, line, the rule it breaks, the fix. No padding, no praise filler.
5. For a refactor, change behaviour never. One rule per pass, a mechanical diff, checks after each pass.
6. Say what was run and what was not (not run on a device, not run live, not run at all).

## Which reference to open

| Touching | Open |
| --- | --- |
| Any code | `references/code.md` |
| TypeScript, React, React Native, Expo | `references/typescript-react-native.md` |
| Swift, SwiftUI, native modules, watchOS | `references/swift.md` |
| Screens, copy, motion, errors, PRDs, design files | `references/product-and-design.md` |
| Secrets, user data, telemetry, auth, databases, APIs | `references/data-and-trust.md` |
| Branches, commits, PRs, merging, deploys, TestFlight, debugging, tests | `references/workflow.md` |
| How to talk to Charles, reports, summaries | `references/communication.md` |

## Top rules (the ones he repeats most)

1. YAGNI. The smallest thing that works, one-liners where they read well, nothing for later.
2. Delete before adding. No unused code, no dead options, no compatibility shims inside an app.
3. Schema-first for anything read from outside the program.
4. One pattern, applied everywhere. Merge duplicates; reuse the existing way.
5. Short domain names. Name what a thing is, not how it is stored.
6. No ternary chains. Lookup table, early returns or an exhaustive `switch`.
7. Tooling enforces style (types, lint, formatter), not comments asking nicely.
8. UI says less: one-line hints, symbols over words, calm wording, red only for real failure.
9. The design and the app agree, and design comes first for anything that looks different.
10. Prove with evidence. Logs and measurements before rebuilds; "done" means verified.
11. Existing failures are yours. Fix what is red even if you did not break it.
12. One branch and PR per body of work. Never merge, delete or deploy before he says done.
13. Never print or commit secrets.
14. Talk plainly, like a homie, and never end with "should I commit/merge/deploy?".

## Final checklist

- [ ] Untrusted data goes through a schema; no hand-rolled guards
- [ ] No nested or multi-way ternaries; no clever one-liner that hides logic
- [ ] Names are short domain nouns; units in numeric names; one word per concept
- [ ] No unused code, params, options, exports, files or dependencies
- [ ] No inline styles; the repo formatter, linter and type check are clean
- [ ] Existing pattern reused; no duplicated helper
- [ ] Copy is one line, calm, and only says what colour or an icon does not
- [ ] Design file and shipped app agree; any new look was designed first
- [ ] Nothing about the person is stored that they cannot see, delete and switch off
- [ ] No secret, key or user content in the repo, build, log or telemetry
- [ ] Full checks ran for every affected package; skipped checks are named
- [ ] Commit, PR and report say plainly what changed, why, and what is unverified
78 changes: 78 additions & 0 deletions skills/chase-review/references/code.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,78 @@
# Code, any language

Marked *(word)* when it is Charles's own instruction, *(inferred)* when read from his reviews.

## Scope and size
- **YAGNI.** Build what is asked now. No option nobody sets, no hook for later, no abstraction for
one caller, no protocol with one implementation and no test seam. *(word, Hominem rules)*
- **One-liner solutions** whenever they stay readable. Fewer lines beat more structure. A one-liner
that hides a decision becomes a named function instead. *(word)*
- **Do not build for hypothetical scale.** Design for the size the product is meant to reach, not a
stress-test number. If a cap or limit is coming, assume it, but do not build the limits until he
opens that discussion. *(word)*
- **Delete, do not comment out.** Dead code, unused exports, stale files and unreferenced
dependencies go. No compatibility alias inside an app; only a published library protects callers.
*(inferred, strong)*
- Fewer dependencies. A new dependency needs a reason a few lines of code cannot meet. Pin it
deliberately and say why in the PR. *(inferred)*

## Names
- Short names that use the **domain noun**: `conversationSchema`, not `conversationRecordSchema`.
Drop words that describe implementation. Keep a longer name only when two things would clash.
*(word)*
- Avoid `Manager`, `Helper`, `Util`, `Handler`, `Info`, `Data`, `Object`, `Service` unless truly the
term. No abbreviations that drop meaning (`msg`, `cfg`, `mgr`); `url`, `id`, `ms` are fine.
- Booleans read as assertions (`isRunning`, `hasPermission`). Put units in numeric names
(`durationMs`, `sampleRate`).
- One concept, one word across the codebase. Rename all of it or none of it.
- The call site reads as a sentence.

## Shape
- **No nested ternaries, and no ternary that picks between more than two results.** Use a lookup
table (`Record<Key, Value>`), a small function with early returns, or a `switch` over a union so
the compiler checks it is exhaustive. One short two-way ternary is fine. A lint rule enforces it
wherever it can. *(word)*
- Early return and `guard` over nested `if`. Flat beats deep.
- Make illegal states unrepresentable: a tagged union with payloads, not a boolean plus two
optionals.
- Small files, one main idea each, named after it. Extract a function when it earns a name, not to
hit a line count.
- **One pattern, applied everywhere.** When the repo already has a way (a store factory, a themed
style hook, a shared schema file, a repository layer), new code uses it. Two copies of the same
helper get merged into one. *(word)*
- Parse once at the boundary, then trust the typed value inside. No defensive checks for states the
types already rule out.

## Comments
- A comment says **why**: an invariant, a constraint, a surprise. Never what the code says.
- Teaching comments are a project choice, not a default. Where a repo has them (Thames's `Swift:`
comments for a TypeScript reader), keep them when editing and add one where a new idea first
appears.
- No change-log comments, no "added for X", no ticket numbers in code.

## Errors
- Typed errors with messages a person can read. A failure keeps what the person has and says what
failed, in calm words (see `product-and-design.md`).
- Never swallow an error silently. Catch only what you can handle at that level.
- Do not retry a failing request to a service on your own unless the repo's rule says so; a service
that is down gets asked over and over. Keep the work, say what failed, offer Try again. Waiting
for a service that is known to be missing sends nothing. *(word, Thames principle 10)*

## Tooling enforces style
- Prefer a type, a lint rule or a script that makes a mistake impossible over a comment asking people
not to make it. *(word)*
- Use the repo's formatter and linter; never add a second one. Format files you changed, not the
whole repo, unless asked.
- If a baseline of known violations exists, shrink it and never grow it.

## Tests
- Test behaviour with real objects where cheap: real files, a real loopback server, an offline audio
engine. Fakes only at true boundaries. *(inferred, Thames)*
- Test names are sentences: `aVoiceMakesTheSphereBiggerThanSilence`.
- A test that only constructs a type, or asserts a private detail, is deleted, not kept for coverage.
- A real-service ("live") test is opt-in behind an env flag and its cost is stated.
- Cover the failure and offline paths, not only the happy one.

## Performance
- Performance is part of the design: 60 fps on a device, or it is not done. Measure on a device;
say so when it was not.
28 changes: 28 additions & 0 deletions skills/chase-review/references/communication.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,28 @@
# How to talk to Charles

## Voice
- **Talk like a homie**: relaxed, warm, casual, a bit of humor, short and real. Not stiff corporate
or textbook language. *(word, 2026-10-20)* Applies to chat, comments, commit messages, PR text
and docs, but casual is the voice, not an excuse to be vague. Keep names and identifiers exact,
and keep anything a tool parses (a conventional-commit subject) intact. A repo's own docs style
wins where it has one.

## Reports
- Lead with what is now true. Then what is not done or not verified. Plain words, no hedging on
things that are verified, no confidence on things that are not.
- Be faithful: if a test failed, say so with the output; if a step was skipped, say that.
- Say what a change does not cover. Do not claim device behaviour that was only read from code.
- Short. No recap of the diff he can read, no list of options you will not pursue. Give a
recommendation, not a survey.
- Do not repeat acknowledged warnings (see `data-and-trust.md`).

## Questions
- Do not ask what comes next, and do not end with "Should I…?". Decide and act.
- Ask only for a real stop: his product or architecture decision, a destructive or outward-facing
action, a missing credential, or a design choice. One decision per question, no recommendation
smuggled inside, buttons for the options.
- When he jokes (the dev build on his phone), take the joke and the point. Fix the cause.

## When he corrects you
- It overrides every file. Do it, then update the relevant reference or memory so it sticks.
- Record the why with the rule, with the date, so later edits know how far it stretches.
49 changes: 49 additions & 0 deletions skills/chase-review/references/data-and-trust.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,49 @@
# Secrets, user data, telemetry, auth, databases, APIs

## Secrets
- **Never print or commit** `.env`, `.env.*`, keys or tokens. Not in logs, PR text, chat or tests.
- A shipped build carries no shared key. Each person signs in and gets a key of their own; the build
script fails if it finds one. Keep real keys out of builds that do not need them.
- Do not repeat the same warning once he has acknowledged it. (Thames: the `.env.testflight` key
note is dropped. Still never print or commit the key.)

## User data
- Say where each piece of data lives and who can read it.
- On-device data is protected, excluded from backups where it should be, and cleared on sign-out.
- Searching the person's own data stays local; the query goes nowhere.
- Removal is easy and can be undone briefly. Nothing is learned that the person cannot see and delete.
- Server-side cleanup (dedupe, merge) must stay reconcilable with the device: keep the periodic
reconcile that lets a worker delete safely.

## Telemetry and crash reports
- Content-free only: closed enums and numbers. **Never add a free string field** to an event.
- Never upload transcripts or conversation logs.
- Default on for TestFlight, off for the App Store until switched on; show a report in full before
it is sent.

## Auth
- One authority for sessions (Better Auth in Hominem). No custom JWT or session code next to it.
- Email-code or passkey flows; browser traffic uses public URLs, server calls the private ones.
- After changing auth config or deployment topology, run the repo's production verify step.

## Databases (Postgres)
- Production-safe evolution: Goose migrations, Kysely, expand/contract, backfills as separate steps.
- Repository and DTO boundary; ownership-scoped mutations.
- After any schema change: migrate local and test databases, then regenerate types.
- Load the repo's database skill before touching schema or repositories. Backups before anything
destructive; ask before it.

## APIs and boundaries
- Validate at the edge with a schema; return typed errors. No trusting the client.
- Reply costs, rate limits and paid calls are visible in code and docs; say what a live test costs.
- Models and vendors: keep the model list in one place, check live model availability and
deprecation dates before choosing, and prefer cheap fast defaults. Never an Anthropic model in
Thames's picker (word, 2026-10-13). A saved choice that is no longer listed falls back to the
default.

## Dev services
- You may start local dev services (Metro, `pnpm dev`, API, workers, databases, Docker) to verify a
change. Confirm before asking him to run anything with `sudo`.
- Fixed-port and proxied services differ (`localhost:4040` vs `https://api.lvh.me`); an
unauthenticated 302 to hosted login is not a service failure.
- A stale running process can load mismatched dependencies; restart it before blaming the code.
72 changes: 72 additions & 0 deletions skills/chase-review/references/product-and-design.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,72 @@
# Product, UI copy, motion, design files

Thames-derived, written to apply to any product Charles builds. Check a design or PRD against this
list, name the principles it leans on, and say where it bends one.

## Experience
1. **One tap to start; voice first** where voice is the product. Anything that can be said needs no
form. A new feature starts from the main control or a quiet row in Settings, not a new screen of
choices.
2. **The person starts and ends every turn.** No auto-listen, no wake word, no silence detection, no
timeout that ends or sends. People with ADHD or who are autistic pause long to think. Make
*starting* easier (Action button, Siri, Watch, headset press); never automate it. *(word)*
3. **Say less.** Hints are one line and never wrap. A state that colour or an icon already says has
no subtext. Prefer a symbol to a word, keep the word for accessibility. Counts appear in one
place. Cut copy first.
4. **Calm, never alarming.** "No service", not "Failed". A lost connection is never "Error". Waiting
is a state with its own shape, not a spinner plus a warning. Red is for real failures only. Every
new state gets calm wording and a no-service version.
5. **It feels physical.** Motion follows the finger and has weight (spring, overshoot, settle).
Haptics mark real boundaries. Reduce Motion always has a calm path. 60 fps measured on a device.
6. **Differentiated, not conventional.** When the standard pattern is the obvious one, look for a
version that is better by taste or technology before settling.

## Component rules (Thames, reuse as defaults)
- The bottom dock never grows: one height in every state. A state that needs more is solved inside
that height with icons. *(word)*
- Every round icon button is a tinted background plus a border in a lighter shade of the same colour,
never a bare fill; buttons in one row are the same size. *(word)*
- A quiet dock has no red at rest. A failure shows in the dock's shape (an icon), not its colour.
*(word)*
- Titles, labels and states shown in the app exist in the design file, and the reverse.

## Trust and honesty
- **Honest.** Only show what is real: no invented content, no counts the app cannot know, no states
it cannot reach. A design shows only what the app can show today, or the board is labelled
`[Not built]`. Costs are visible behind a small control.
- **You own what it knows.** Memory-like features are off by default, visible, deletable, with a
brief undo. Anything that adds data about the person has a place to see it, delete it, and a clear
off switch.
- **Works without service.** What is on the device shows at once and works offline; sync is quiet.
Anything saved offline is kept and sent later.
- **The person is never the error handler.** Retries, syncing and reconciling are the app's job;
bent only for failed service requests (see `code.md`, Errors).
- Origin and voice matter: lean on the real story of the product in copy, not generic "AI assistant"
language. Thames: thinking out loud on a walk along a river.

## Design first (a rule, not a preference)
Never change code for a feature until its design is agreed.
1. Make **at least three radically different options** (a different idea, not a different colour or
spacing). Put them in the feature's "Options considered" section. For animation needing live
playback, make alternatives in the motion lab and link the study.
2. He chooses. Keep every option where it is, label the chosen one "(A chosen)", and update the
built screens (all states, every screen the feature touches) to show only the agreed design.
Controls used by more than one feature go in Components. Record timing and easing for motion.
3. Only then change code. If code needs something the design does not show, go back to the design.
- Exempt: a bug fix with no visible change. Not exempt: any change to how something looks, however
small. Say what the three directions would be and ask which.
- Read the repo's principles skill (`product-principles` in Thames) before drawing any option.
- Design files are organised by what a person looks for, not by device: Core (mechanics that do not
change), Components (shared pieces), one page per feature. The design file is the source of truth;
the motion lab is the review surface for animation.

## PRDs
- Live in `docs/prd/`. Read the thesis and principles first; name the principles leaned on and any
bent. Answer open questions with them.
- When work lands, tick the PRD's Progress list and add to its Decision log in the same PR. If a
screen changes, update the design file first; if motion changes, update the motion study first.
- Unresolved decisions are marked `OPEN — USER DECISION REQUIRED`, not decided silently.

## Teach as we go
- Where the repo has a teaching layer (course, blog, teaching comments), keep it in step with the
code. Understanding is part of the product of building it.
Loading
Loading