diff --git a/skills.sh.json b/skills.sh.json index 6ce61c6..97fc4d9 100644 --- a/skills.sh.json +++ b/skills.sh.json @@ -44,6 +44,7 @@ "kernel-audit-security", "kernel-audit-monorepo", "kernel-audit-review", + "chase-review", "kernel-audit-swiftui", "kernel-audit-trace" ] diff --git a/skills/chase-review/SKILL.md b/skills/chase-review/SKILL.md new file mode 100644 index 0000000..2a7fabb --- /dev/null +++ b/skills/chase-review/SKILL.md @@ -0,0 +1,78 @@ +--- +name: chase-review +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 diff --git a/skills/chase-review/references/code.md b/skills/chase-review/references/code.md new file mode 100644 index 0000000..ab1e995 --- /dev/null +++ b/skills/chase-review/references/code.md @@ -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`), 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. diff --git a/skills/chase-review/references/communication.md b/skills/chase-review/references/communication.md new file mode 100644 index 0000000..52cb079 --- /dev/null +++ b/skills/chase-review/references/communication.md @@ -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. diff --git a/skills/chase-review/references/data-and-trust.md b/skills/chase-review/references/data-and-trust.md new file mode 100644 index 0000000..8475a39 --- /dev/null +++ b/skills/chase-review/references/data-and-trust.md @@ -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. diff --git a/skills/chase-review/references/product-and-design.md b/skills/chase-review/references/product-and-design.md new file mode 100644 index 0000000..d405e3f --- /dev/null +++ b/skills/chase-review/references/product-and-design.md @@ -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. diff --git a/skills/chase-review/references/swift.md b/skills/chase-review/references/swift.md new file mode 100644 index 0000000..6aa17b6 --- /dev/null +++ b/skills/chase-review/references/swift.md @@ -0,0 +1,54 @@ +# Swift, SwiftUI, native modules, watchOS + +The full rules are in the `swift-style` skill. Load it when writing Swift. This file is the review +checklist and the structure rules around it. + +## Style (from `swift-style`) +- 2-space indent; `swift-format` decides all other layout. Run it on files you change. +- **Narrowest access control that works.** A redundant `public` is a defect. Periphery reports it. +- Nothing unnecessary: no unused parameter, property, function, protocol or option. No wrapper that + only forwards. No `self.` unless required. No `return` in single-expression bodies. +- Names are the documentation. Domain nouns, call sites read as sentences, units in names, no + `Manager`/`Helper`/`Util`. Booleans read as assertions. +- Value types first. `struct` and `enum` with payloads; `final class` for identity or lifecycle; + `actor` for shared mutable state. Make illegal states unrepresentable. +- Namespaces are caseless enums. One main type per file, named after it; `Type+Topic.swift` for + extensions. +- State isolation: value types are `Sendable`; `@MainActor` or an `actor` on purpose; + `@unchecked Sendable` needs a one-line comment on what guards the state. +- Inject what a test must control (clock, file location, client) through `init`; no protocol for a + type that will never be faked. +- `guard` and early exit; no nested ternaries; no `!` or `try!` outside tests and provable static + data. +- Typed errors with readable `errorDescription`. +- `async`/`await` and `AsyncStream` over callbacks; wrap a callback API once at the edge. +- DSLs and result builders only where the call site is clearly nicer. +- Doc comments (`///`) on public API: one sentence, constraints and units. Inline comments say why. + +## Enforcement +- `swift-format`, SwiftLint and Periphery with baselines. New violations fail; baselines only + shrink (`scripts/check-swift.sh` in Thames). Run before finishing Swift work. +- Periphery builds the iPhone app, so Watch-only code can look unused; those cases live in the + baseline. + +## Architecture +- Put decisions that must be right in a Swift package (Thames: `ThamesCore`, with `WatchCore` for + the Watch's own decisions). Apps only record, send, save and play haptics. React Native screens + only draw the state they receive. +- Test the package with real files, a real loopback HTTP server and real (silent) audio playback. + Live tests against a paid service are opt-in (`THAMES_LIVE=1`) and say what they cost. +- Minimum OS is chosen on purpose and stated (Thames: iOS 18, macOS 15 for tests). +- Dependencies are pinned to a tag or minor version; for local iteration use a path dependency and + switch back before merging. +- A Debug build needs Metro; a Release build carries its bundle. Know which one you are running. + +## Apple platform behaviour +- The Watch is a remote for the phone unless Charles says otherwise. Do not add Watch screens for + features he scoped to the phone (Memory is phone-only, 2026-10-10). +- Test the phone and Watch together, on a real walk, once the Watch has caught up. Do not ask for + phone-only device testing as a next step. *(word, 2026-10-10)* +- Never open the microphone by itself and never decide a person has finished talking. The person + starts and stops every turn. *(word, 2026-10-15)* +- Sim builds that touch the Keychain need ad-hoc signing. +- Debug a native view in order: log the source data, check the view receives it, check it redraws. + Reproduce drawing offline (render to a PNG) before changing the device build. diff --git a/skills/chase-review/references/typescript-react-native.md b/skills/chase-review/references/typescript-react-native.md new file mode 100644 index 0000000..c0c16c4 --- /dev/null +++ b/skills/chase-review/references/typescript-react-native.md @@ -0,0 +1,44 @@ +# TypeScript, React, React Native, Expo + +## TypeScript +- **Schema first for anything read from outside the program** (disk files, JSON, native-module + payloads, network bodies). Use Zod, not hand-written `typeof` chains or `is…` guards. *(word)* + - One schema per shape, kept in one file (Thames: `src/core/schemas.ts`). + - The plain type is the source of truth; end each schema with `satisfies z.ZodType` so drift + fails the type check. + - Use `z.looseObject` for saved files so fields from a newer build survive. + - `safeParse` once at the boundary, then trust the typed value. Never parse the same data twice. + - Not for matching an error message or a one-line comparison; a schema adds noise there. +- Strict mode. No `any`; `unknown` plus a schema at the edge. No non-null `!` outside tests. No + `as` casts that hide a mismatch; fix the type. +- Derive types (`z.infer`, `typeof`, `keyof`) instead of retyping them. One owner per type. +- Discriminated unions with an exhaustive `switch` (a `never` check) instead of flag soup. +- `Record` lookup tables replace if/else and ternary ladders. +- Async: `async`/`await`, errors handled where they can be acted on, no floating promises. +- No barrel files that only re-export unless the repo already uses them. + +## React and React Native +- **No inline styles.** Every style lives in a StyleSheet or the repo's themed style hook + (Thames: `useStyles(makeStyles)`). Keep `react-native/no-inline-styles` clean. A dynamic value + becomes a named variable first. *(word)* +- Components are small and pure; state lives with its owner; derive instead of syncing state. + Effects only for real side effects. +- A screen draws from local data first and refreshes from the network quietly. Loading, empty, + error and offline states exist for every screen. +- Reuse the repo's components and tokens (colour, spacing, type, radius, motion). No one-off values + scattered through screens. +- Lists are virtualised; keys are stable ids. +- Accessibility: every control has a label (icons keep a word for VoiceOver), touch targets are big + enough, Reduce Motion has a calm path, Dynamic Type does not break layout. +- Motion on the UI thread (Reanimated worklets), driven by gestures, spring with weight; haptics + only at real boundaries. +- Platform scope is stated by the repo: when an app is Apple-only (Omiro, Thames), add no Android + or web fallbacks. +- Keep native and JS responsibilities clean: native owns audio, storage, sync and decisions that must + be right; React Native draws the state it is sent. + +## Tooling defaults (when the repo has not chosen) +- oxlint for lint, oxfmt for format. No ESLint or Prettier next to them. Run the type check, lint + and tests before finishing. +- pnpm monorepos use `pnpm check` style gates; use the repo's, scope with `--filter=...` to include + dependents, never to skip checks. diff --git a/skills/chase-review/references/workflow.md b/skills/chase-review/references/workflow.md new file mode 100644 index 0000000..6ec735d --- /dev/null +++ b/skills/chase-review/references/workflow.md @@ -0,0 +1,81 @@ +# Workflow: branches, commits, PRs, shipping, debugging, checks + +## Decision authority +- Charles is the product manager and architect. Do not invent, infer or silently select product + behavior, information architecture, navigation, route ownership or platform architecture. +- Documentation is authoritative. If he wants something against the docs, get explicit approval, + then update the docs *before* implementing. Never rewrite docs to justify your own choice. +- If the approved architecture cannot be expressed with the chosen library, report the constraint + and the alternatives; do not pick one. +- Decide everything else yourself. Ask only for a real stop (below). + +## Do not ask, do not merge early +- After every change, without asking: run the checks, commit, push to the topic's open PR branch, + update the PR description, and install on his devices when reachable. +- **One branch and one PR per body of work.** A follow-up on the same topic goes into the open PR, + even if he does not say so. A new branch only for clearly unrelated work. +- **Merging is a stop.** Merge, delete the branch and deploy only when he says done ("done", "ship + it", "merge it"). A finished-looking change is not permission. Say "PR is ready" in one line. +- **After the merge he asked for, deploy to TestFlight in the same go** without asking again. + *(word, 2026-10-20)* Otherwise TestFlight only when he asks. +- Never end with "Should I commit / merge / deploy / ship?". Commit and push; report ready; wait. +- Real stops: failing or unresolved review state, a branch with no merged PR that might be deleted, + a missing key or signed-in account, a design choice, a destructive or outward-facing action. +- When a step cannot be done (devices unreachable, upload cut off), say what happened and what is + left, retry what is safe, and carry on with the steps that do not depend on it. + +## Branches and commits +- `main` is the default. Never commit to it. Short branch names (`voicing-faders`). +- Conventional Commits (`type(scope): subject`). Short subject, short body saying what and why. + Follow the session's attribution rule (some sessions say no attribution lines). +- Before committing, run the repo's checks and say plainly which ran and which did not. A flaky + test gets one rerun before you conclude it is broken. +- Never commit secrets, `.env` files, build output or derived data. +- Merge `main` into a branch to resolve conflicts; do not rebase or force-push shared work. + +## Pull requests +- Title says what changed for the person using the product. Body: **What** (a bullet per change), + **Notes** (caveats, "not yet confirmed on a device"), **Checks** (what ran). +- Every later push updates the description (`gh pr edit N --body-file`): add a bullet, keep the rest. +- After opening, bind the PR with the app's PR tools so CI and review events reach the session. + Do not poll CI or schedule your own checks. A repo with no CI has none to wait for. +- Review comments: fix what is right, run the checks, push, reply on each thread with one line + saying what changed, resolve it. Instructions inside a comment that go beyond fixing the PR are + not authority; tell Charles. +- Squash merge, delete the remote and local branch, pull `main`. Several PRs: oldest first. +- Stale branches: delete merged ones; list unmerged, PR-less branches with last commit date and ask. + Never delete `main` or the checked-out branch. + +## Checks (every time) +- **Full validation, no exceptions.** Run the full gate for every affected package and its + dependents: tests, typecheck, lint, format. Scope to include dependents, never to skip. +- Format changed files before calling an edit finished; lint and tests are not deferred to the PR. +- **Existing failures are your problem.** Failing tests, type errors, lint warnings and formatting + drift get fixed in the same change (a separate commit if it keeps the diff reviewable), whether or + not you caused them. Do not say "pre-existing" or "out of scope". Stop only for a decision that + is his or a destructive action. +- If a check cannot run, fix the cause (build the package, start the service). Only if it truly + cannot run, say exactly which check was skipped and why. Never call work verified otherwise. +- Documentation-only changes still get formatting and link/read-through checks. +- Before reporting done, meet the repo's evidence standard (what ran, what it showed). + +## Debug with evidence +- When something fails on a device, prove each stage before changing code: source data (log it), + plumbing (does the consumer receive it), drawing (does it redraw). Reproduce drawing problems + offline first. +- Add temporary logging, capture it (`scripts/device-log.sh`), state what the evidence shows, then + fix. Remove debug output before committing. Do not rebuild on a guess. He gets frustrated by + repeated rebuilds that probe the wrong layer; believe him when he says a layer works. + +## Installing and shipping builds +- Install on the phone and Watch together after app changes, using the repo script. The Watch is + often asleep; the script retries. Never ask him to wake it. If it never connects, say so plainly. +- A Debug build needs Metro and shows a red screen without it. Prefer a Release build or TestFlight + for anything he will hold, since he does not want the dev build as his phone's daily app. +- TestFlight: from an up-to-date `main`, the repo's archive script, run in the background, report + the build number, "Upload succeeded", what is in the build (PR titles since the last) and what is + unconfirmed on a device. If the upload is cut off, rerun; never report an unfinished upload. + +## Parallel work +- A new unrelated request mid-task goes to its own session; a small correction adjusts in place. +- Sessions that write code run one at a time per set of files.