Keep the personal half of the desktop in one place, and ask before installing it

Agent instructions, skills, SSH host aliases and expansion triggers are worth
having identical on every machine one person owns, and belong in none of the
shared configuration. They live in user/ now, with a manifest saying where each
piece goes and a link-user stage that puts it there.

That stage does nothing unless the machine said yes. Somebody who clones Panama
to try the desktop keeps their own ~/.claude/CLAUDE.md exactly where it was;
the question names the destinations and defaults to no. Anything displaced goes
to config/old rather than being deleted.

~/.claude/CLAUDE.md and ~/.codex/AGENTS.md were byte-identical copies of one
file, which is the drift this exists to prevent.

Also adds the vitals toggles for the battery and Claude usage readouts, which
had preferences and no way to reach them.
This commit is contained in:
Gabriel Brown
2026-08-22 08:54:43 -04:00
parent 8b96d907a1
commit 89761a7da3
156 changed files with 16439 additions and 6 deletions
+59
View File
@@ -0,0 +1,59 @@
# When to mock
Mock at **system boundaries** only:
- External APIs you do not control (Jira, Stripe, email, Infisical)
- Time and randomness
- Databases, sometimes, though a real local test instance is better where one exists
- The filesystem, sometimes
Do **not** mock your own modules, internal collaborators, or anything you control. A test that mocks
what you own is asserting on your own wiring rather than on behaviour, and it will break on the first
refactor that changes nothing a caller can see.
Before mocking any dependency, understand what it actually does. A mock built on a guess about a
dependency's side effects encodes the guess into the suite.
## Designing for substitutability
### Accept dependencies, do not construct them
```typescript
// Easy to substitute at the seam
function processPayment(order: Order, paymentClient: PaymentClient) {
return paymentClient.charge(order.total);
}
// Welded in: nothing can stand in for the client
function processPayment(order: Order) {
const client = new StripeClient(process.env.STRIPE_KEY);
return client.charge(order.total);
}
```
### Prefer an SDK-shaped interface over a generic fetcher
One function per external operation, rather than a single generic call with conditional logic:
```typescript
// GOOD: each operation is independently substitutable
const api = {
getUser: (id: string) => fetch(`/users/${id}`),
getOrders: (userId: string) => fetch(`/users/${userId}/orders`),
createOrder: (data: NewOrder) => fetch("/orders", { method: "POST", body: data }),
};
// BAD: the stand-in needs conditional logic to know what it is being asked for
const api = {
fetch: (endpoint: string, options: RequestInit) => fetch(endpoint, options),
};
```
The SDK shape means each stand-in returns one specific type, no branching in test setup, an obvious
read of which endpoints a test exercises, and type safety per operation.
## One adapter is not a seam
Introducing a port because a single test wants to substitute at it is indirection, not design. One
adapter means a hypothetical seam; two means a real one, and production-plus-test counts as two. Call
the Skill tool with "codebase-design" when the seam placement itself is in question.
+136
View File
@@ -0,0 +1,136 @@
---
name: tdd
description: Use when implementing any feature or bugfix, before writing implementation code, and when deciding what to test, where a seam goes, or whether a mock is warranted.
---
# Test-Driven Development
Write the test first. Watch it fail. Write the minimal code that passes it.
**If you did not watch the test fail, you do not know that it tests the right thing.** A test written
after the code passes immediately, which proves nothing: it may be testing the implementation, or the
wrong behaviour, or nothing at all.
**Violating the letter of the rule is violating its spirit.**
## The Iron Law
```
NO PRODUCTION CODE WITHOUT A FAILING TEST FIRST
```
Wrote the code first? Delete it and start over. Not kept as reference, not adapted while you write
the test, not consulted. Delete means delete, and implement fresh from the test.
Exceptions worth asking about: throwaway prototypes, generated code, config files. Thinking "skip it
just this once"? That thought is the rationalization, not the exception.
## Agree the seams before writing anything
A **seam** is the public boundary you test at: the interface where behaviour is observable without
reaching inside. Tests live at seams, never against internals.
**Test only at pre-agreed seams.** Before writing any test, name the seams under test and confirm
them:
> "The public interface here is `X`. I plan to test at `X` and `Y`, and not at `Z` because it is
> internal. Does that match what you expect?"
No test gets written at an unconfirmed seam. This is the whole mechanism that keeps a suite focused:
you cannot test everything, so agreeing the seams up front is what puts the effort on the critical
paths and the complex logic instead of spreading it thin across every edge case.
When the shape of that interface is itself the open question, how deep the module should be, where
the seam belongs, what the interface exposes, call the Skill tool with "codebase-design" and use that
vocabulary. It is a reference to consult, not a session to run.
## The loop
Run this once per behaviour. One seam, one test, one minimal implementation per cycle.
1. **RED: write one failing test.** One behaviour, a name that describes it, real code rather than
mocks. See [TESTS.md](TESTS.md) for what a good one looks like.
2. **Verify RED. Mandatory, never skipped.** Run the project's test command for that single file.
Confirm it *fails* rather than errors, that the message is the one you expected, and that it fails
because the behaviour is missing rather than because of a typo. A test that passes immediately is
testing behaviour that already exists: fix the test. A test that errors is broken: fix it and
re-run until it fails correctly.
3. **GREEN: write the simplest code that passes.** Nothing speculative, no options objects for
futures nobody asked for, no improving adjacent code.
4. **Verify GREEN. Mandatory.** The test passes, the other tests still pass, and the output is
pristine with no stray errors or warnings. Test still failing? Fix the code, never the test.
5. **Tidy, then stop.** Remove duplication you just introduced and improve names, keeping every test
green. **Design refactoring is not part of this loop**: reshaping a module, moving a seam, or
extracting an abstraction belongs to review, not to the red-green cycle. Confusing the two is how
a cycle turns into an afternoon.
6. **Repeat** with the next failing test.
Look up the test command rather than assuming one: `package.json` scripts are the source of truth,
and these repos are usually `bun test <path>` or `bunx vitest run <path>`.
## Work in vertical slices
**Horizontal slicing is the anti-pattern**: writing all the tests first, then all the implementation.
Bulk tests verify *imagined* behaviour. You end up testing the shape of things rather than what a
caller experiences, the tests go insensitive to real changes, and you commit to a test structure
before you understand the implementation.
Work vertically instead: one test, one implementation, repeat. Each test is a **tracer bullet** that
responds to what the last cycle taught you.
## Mocking
Mock at system boundaries only, never your own modules. See [MOCKING.md](MOCKING.md) for the boundary
list and how to design an interface that is easy to substitute at.
## Rationalizations
| Excuse | Reality |
|---|---|
| "Too simple to test" | Simple code breaks. The test takes 30 seconds. |
| "I'll test after" | Tests written after pass immediately, which proves nothing. You never watched it fail, so you never proved it can catch anything. |
| "Tests after achieve the same thing, spirit not ritual" | Tests-after answer "what does this do?"; tests-first answer "what should this do?" Writing them after biases you toward the cases you already remembered. |
| "I already tested it manually" | Manual testing leaves no record, cannot be re-run, and is the first thing dropped under pressure. |
| "Deleting hours of work is wasteful" | Sunk cost. That time is spent either way. Keeping code you cannot trust is the waste. |
| "I'll keep it as reference and write tests first" | You will adapt it, which is testing after. Delete means delete. |
| "I need to explore first" | Fine. Throw the exploration away and start with TDD. |
| "This is hard to test" | Listen to the test. Hard to test means hard to use: the design is talking to you. |
| "TDD will slow me down" | The shortcut means debugging in production, which is slower. |
| "The existing code has no tests" | You are improving it. Add them. |
## Red flags: stop and start over
- Code written before the test
- The test passed the first time you ran it
- You cannot explain why the test failed
- "I'll add tests later"
- "It's about the spirit, not the ritual"
- "Keep it as reference"
- "This one is different because..."
All of these mean the same thing: delete the code, start again with the test.
## When stuck
| Problem | What it means |
|---|---|
| Do not know how to test it | Write the API you wish existed, then the assertion. Ask if still stuck. |
| The test is too complicated | The design is too complicated. Simplify the interface. |
| You have to mock everything | The code is too coupled. Inject dependencies instead. |
| The setup is enormous | Extract helpers. Still enormous? The design needs work. |
## Fixing a bug
Never fix a bug without a test. Write the failing test that reproduces it, watch it fail, then fix.
The test proves the fix and prevents the regression. Where the bug is hard to reproduce at all, the
loop-building discipline is its own skill: call the Skill tool with "diagnosing-bugs" first, then come
back here once you have a red-capable repro.
## Done when
- Every new behaviour has a test that was watched failing, for the expected reason, before the code
existed.
- Every test sits at a seam that was agreed before it was written.
- The full suite passes and the output is pristine.
- No test asserts on a mock of something you own, and no expected value was computed the way the
code computes it.
+97
View File
@@ -0,0 +1,97 @@
# What a good test is
A test verifies behaviour through a public interface. The code underneath can change entirely; the
test should not. A good one reads like a specification: `user can checkout with valid cart` tells you
exactly what capability exists, and survives refactors because it does not care about internal
structure.
## Good
```typescript
test("user can checkout with valid cart", async () => {
const cart = createCart();
cart.add(product);
const result = await checkout(cart, paymentMethod);
expect(result.status).toBe("confirmed");
});
```
- Tests what a caller cares about
- Uses the public interface only
- Survives internal refactors
- Describes *what*, not *how*
- One logical assertion
## The three anti-patterns
### Implementation-coupled
Mocks internal collaborators, reaches for private methods, or asserts on call counts and ordering.
**The tell: the test breaks when you refactor, even though the behaviour did not change.**
```typescript
// BAD: asserts on the mock, not the behaviour
test("checkout calls paymentService.process", async () => {
const mockPayment = vi.mock(paymentService);
await checkout(cart, payment);
expect(mockPayment.process).toHaveBeenCalledWith(cart.total);
});
```
A second form is verifying through a side channel instead of the interface:
```typescript
// BAD: bypasses the interface to check the database directly
test("createUser saves to database", async () => {
await createUser({ name: "Alice" });
const row = await db.query("SELECT * FROM users WHERE name = ?", ["Alice"]);
expect(row).toBeDefined();
});
// GOOD: verifies through the interface a caller would use
test("createUser makes the user retrievable", async () => {
const user = await createUser({ name: "Alice" });
const retrieved = await getUser(user.id);
expect(retrieved.name).toBe("Alice");
});
```
### Tautological
The expected value is recomputed the way the code computes it, so the test passes by construction and
can never disagree with the implementation.
```typescript
// BAD: the assertion reimplements the function
test("calculateTotal sums line items", () => {
const items = [{ price: 10 }, { price: 5 }];
const expected = items.reduce((sum, i) => sum + i.price, 0);
expect(calculateTotal(items)).toBe(expected);
});
// GOOD: an independent, known literal
test("calculateTotal sums line items", () => {
expect(calculateTotal([{ price: 10 }, { price: 5 }])).toBe(15);
});
```
Expected values come from an independent source of truth: a known-good literal, a worked example, or
the spec. Never from the code.
### Horizontal slicing
Writing all the tests first, then all the implementation. Covered in `SKILL.md`; the fix is vertical
slices, one test and one implementation at a time.
## Before writing any test
Name the production change that would make this test fail. If you cannot name one, the test asserts
nothing and should not be written.
## Keep test-only code out of production
A method that exists so a test can reach inside is a hole in the interface. Put helpers in test
utilities. If the test cannot reach what it needs through the interface, that is a design finding,
not a reason to widen the interface.
@@ -0,0 +1,3 @@
interface:
display_name: TDD
short_description: Red-green loop, pre-agreed seams, and what makes a test worth keeping.