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
204 changes: 102 additions & 102 deletions .agents/skills/code-review/repo-checklist.md
Original file line number Diff line number Diff line change
Expand Up @@ -10,170 +10,170 @@ Item format (upstream checklist-schema v2.3):

```markdown
- [ ] Short description (source: path)
severity: P0|P1|P2|P3
scope: universal|project|module
frequency: 1x|2x+|chronic
severity: P0|P1|P2|P3
scope: universal|project|module
frequency: 1x|2x+|chronic
```

Sort on load: chronic > 2x+ > 1x, then P0 > P1 > P2 > P3, then universal > project > module.

## Security & keys

- [ ] Private keys only in the Hardhat keystore or Scaleway secrets — never in code, tests, fixtures, logs, or commits (source: AGENTS.md → Security)
severity: P0
scope: universal
frequency: chronic
severity: P0
scope: universal
frequency: chronic

- [ ] Any code calling GenImNFTv4's requestImageUpdate() must verify isAuthorizedAgent() first — the CVE-2025-11-26 pattern (source: eth/AGENTS.md → Security)
severity: P0
scope: project
frequency: 1x
severity: P0
scope: project
frequency: 1x

- [ ] EIP-712 domain names differ by network: mainnet USDC is "USD Coin", testnet is "USDC" — check the verified per-network table before touching payment signing (source: AGENTS.md → scw_js/README.md, Adding New Networks)
severity: P0
scope: module
frequency: 1x
- [ ] EIP-712 domain names differ by network: mainnet USDC is "USD Coin", testnet is "USDC" — check the verified per-network table before touching payment signing (source: AGENTS.md → x402 payments; scw_js/README.md → Adding New Networks)
severity: P0
scope: module
frequency: 1x

- [ ] Serverless secrets are configured via Scaleway Console only; `env:` in serverless.yml is public by definition (source: AGENTS.md → Serverless)
severity: P1
scope: universal
frequency: 2x+
- [ ] Serverless secrets are configured via Scaleway Console only; `env:` in serverless.yml is public by definition (source: scw_js/AGENTS.md → Serverless conventions; same rule in the other serverless packages' AGENTS.md)
severity: P1
scope: universal
frequency: 2x+

- [ ] All serverless responses include CORS headers (Access-Control-Allow-Origin: *) (source: AGENTS.md → Security)
severity: P1
scope: universal
frequency: chronic
- [ ] All serverless responses include CORS headers — `Access-Control-Allow-Origin: *` for public endpoints; analytics uses a deliberate origin whitelist, never normalize it to `*` (source: AGENTS.md → Security; analytics/README.md → API)
severity: P1
scope: universal
frequency: chronic

- [ ] Changes touching auth, payments, or keys are checked against .github/THREAT_MODEL.md for blast radius before approval (source: AGENTS.md → Security)
severity: P1
scope: project
frequency: 2x+
severity: P1
scope: project
frequency: 2x+

## Payments & x402

- [ ] The x402 role map: /imagegen and /assistent are buyers; the *-agent.fretchen.eu endpoints are sellers — any PR or doc that names a role gets this wrong half the time (source: .agents/skills/x402/SKILL.md)
severity: P1
scope: project
frequency: chronic
- [ ] The x402 role map: /imagegen and /assistent are buyers; the \*-agent.fretchen.eu endpoints are sellers — any PR or doc that names a role gets this wrong half the time (source: .agents/skills/x402/SKILL.md)
severity: P1
scope: project
frequency: chronic

- [ ] Channel identity: wallet + voucher signer + receiver + token + network hash into channelId — code that recomputes any of these mid-turn silently opens a second channel the user must fund again (source: website/utils/x402PaidFetch.ts)
severity: P1
scope: module
frequency: 1x
severity: P1
scope: module
frequency: 1x

## OpenAPI / serverless handlers

- [ ] Published OpenAPI strictness must match handler behavior: z.object only for bodies built field by field, z.looseObject when unknown keys are forwarded or ignored (source: AGENTS.md → Serverless; enforced by test/openapi_*_generation.test.ts)
severity: P1
scope: universal
frequency: 2x+
- [ ] Published OpenAPI strictness must match handler behavior: z.object only for bodies built field by field, z.looseObject when unknown keys are forwarded or ignored (source: AGENTS.md → x402 payments; enforced by test/openapi\_\*\_generation.test.ts)
severity: P1
scope: universal
frequency: 2x+

## Contracts (eth/)

- [ ] UUPS upgradeable proxy pattern — never deploy an implementation contract directly (source: eth/AGENTS.md → Upgrades)
severity: P0
scope: module
frequency: 1x
severity: P0
scope: module
frequency: 1x

- [ ] Upgrades append new state variables after existing ones only — reordering or inserting corrupts proxy storage (source: eth/AGENTS.md → Upgrades)
severity: P0
scope: module
frequency: 1x
severity: P0
scope: module
frequency: 1x

- [ ] Hardhat here uses Viem, not Ethers: contract calls return bigint, format with viem helpers (source: eth/AGENTS.md)
severity: P2
scope: module
frequency: chronic
severity: P2
scope: module
frequency: chronic

- [ ] Tests go in the right category: *_Functional.ts (Viem, contract logic) vs *_Deployment.ts (ethers + upgrades plugin, script coverage) — never both libraries in one file (source: eth/AGENTS.md → Tests)
severity: P2
scope: module
frequency: 2x+
- [ ] Tests go in the right category: _\_Functional.ts (Viem, contract logic) vs _\_Deployment.ts (ethers + upgrades plugin, script coverage) — never both libraries in one file (source: eth/AGENTS.md → Tests)
severity: P2
scope: module
frequency: 2x+

- [ ] Deploy/upgrade scripts export their deploy function and guard direct execution, so the *_Deployment.ts import does not run a deployment (source: eth/AGENTS.md → Deployment scripts)
severity: P2
scope: module
frequency: 2x+
- [ ] Deploy/upgrade scripts export their deploy function and guard direct execution, so the \*\_Deployment.ts import does not run a deployment (source: eth/AGENTS.md → Deployment scripts)
severity: P2
scope: module
frequency: 2x+

- [ ] After changing a contract: run npx hardhat run scripts/export-abi.ts, then update the website's ABI imports from eth/abi/contracts/*.ts (source: AGENTS.md → Commands)
severity: P2
scope: project
frequency: 2x+
- [ ] After changing a contract: run npx hardhat run scripts/export-abi.ts, then update the website's ABI imports from eth/abi/contracts/\*.ts (source: AGENTS.md → Commands)
severity: P2
scope: project
frequency: 2x+

## Website (Vike + React 19 + Panda CSS)

- [ ] Panda compiles css({}) statically — the six silent-failure rules (variable values, spacing shorthands, unknown tokens, token() outside css({}), fontFamily faces, recipe variants as variables) pass tsc AND tests while the CSS is simply missing; bulk style changes are verified by diffing emitted declarations (source: website/AGENTS.md; enforced by test/styleConventions.test.ts)
severity: P1
scope: module
frequency: chronic
- [ ] Panda compiles css({}) statically — the six silent-failure rules (variable values, spacing shorthands, unknown tokens, token() outside css({}), fontFamily faces, recipe variants as variables) pass tsc AND tests while the CSS is simply missing; bulk style changes are verified by diffing emitted declarations (source: .agents/skills/panda/SKILL.md; enforced by test/styleConventions.test.ts)
severity: P1
scope: module
frequency: chronic

- [ ] Client-only components are imported with { ssr: false } (source: website/AGENTS.md)
severity: P2
scope: module
frequency: chronic
severity: P2
scope: module
frequency: chronic

- [ ] npm run prepare (Panda codegen) after config changes; styled-system/ is generated — never edited directly (source: website/AGENTS.md)
severity: P2
scope: module
frequency: 2x+
severity: P2
scope: module
frequency: 2x+

- [ ] Wagmi hooks are auto-generated from wagmi.config.ts, not hand-written (source: website/AGENTS.md)
severity: P2
scope: module
frequency: 1x
severity: P2
scope: module
frequency: 1x

- [ ] Never read useAccount().isConnected directly — go through useIsWalletConnected (source: website/hooks/useIsWalletConnected.ts; enforced by test/walletConnectionConvention.test.ts)
severity: P2
scope: module
frequency: chronic
severity: P2
scope: module
frequency: chronic

- [ ] The design system in website/README.md is read before adding any style — colors and their jobs, the button recipe, the scales (source: website/AGENTS.md)
severity: P2
scope: module
frequency: 2x+
severity: P2
scope: module
frequency: 2x+

## Assistant chat tools (website/tools/, AssistantChat.tsx)

- [ ] Every TOOL_REGISTRY entry needs a toolRunners entry — the test suite iterates the registry, so a tool without a runner fails the suite (source: .agents/skills/chat-tools/SKILL.md)
severity: P1
scope: module
frequency: 2x+
severity: P1
scope: module
frequency: 2x+

- [ ] website/tools/*.ts stay React-free — no hooks, no React imports; that is what keeps them importable from non-browser callers (source: .agents/skills/chat-tools/SKILL.md)
severity: P1
scope: module
frequency: 2x+
- [ ] website/tools/\*.ts stay React-free — no hooks, no React imports; that is what keeps them importable from non-browser callers (source: .agents/skills/chat-tools/SKILL.md)
severity: P1
scope: module
frequency: 2x+

- [ ] Tool definitions and results are input tokens on every later hop: serialized tools stay under MAX_TOOLS_BYTES (8192), results projected hard, and `tools: []` is never sent — pass undefined so the key is absent (source: .agents/skills/chat-tools/SKILL.md)
severity: P1
scope: module
frequency: chronic
severity: P1
scope: module
frequency: chronic

- [ ] A non-ok tool status withdraws the tool for the rest of the turn unless it is an answer-like status marked recoverable: true (source: .agents/skills/chat-tools/SKILL.md)
severity: P1
scope: module
frequency: 2x+
severity: P1
scope: module
frequency: 2x+

## Blog

- [ ] No MDX before an approved website/blog/<slug>.plan.md exists (source: AGENTS.md → Blog Posts; .agents/skills/blog-planner/SKILL.md)
severity: P1
scope: project
frequency: 2x+
- [ ] No MDX before an approved website/blog/<slug>.plan.md exists (source: website/AGENTS.md → Blog posts; .agents/skills/blog-planner/SKILL.md)
severity: P1
scope: project
frequency: 2x+

- [ ] Prose carries no manufactured suspense: concede-then-swing, forward promise, meta-announcement, and withheld reveal are banned — figure captions included (source: AGENTS.md → Blog Posts)
severity: P2
scope: project
frequency: chronic
- [ ] Prose carries no manufactured suspense: concede-then-swing, forward promise, meta-announcement, and withheld reveal are banned — figure captions included (source: website/AGENTS.md → Blog posts)
severity: P2
scope: project
frequency: chronic

## Process

- [ ] Branch off origin/main with --no-track; set tracking on first push with -u — auto-setup makes the new branch track origin/main and can misdirect pushes (source: AGENTS.md → Git Workflow)
severity: P2
scope: project
frequency: 2x+
severity: P2
scope: project
frequency: 2x+

- [ ] shared/chain-utils is a local file dependency — build it before rebuilding any dependent package (source: AGENTS.md → Commands)
severity: P2
scope: project
frequency: 2x+
severity: P2
scope: project
frequency: 2x+
123 changes: 123 additions & 0 deletions .agents/skills/panda/SKILL.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,123 @@
---
name: "panda"
description: "Use when: writing or changing any style in website/ — css({}) calls, recipes or patterns, adding a token to panda.config.ts, or debugging an element that renders a class name but no CSS. Carries the six ways Panda fails silently in this repo (the component renders, tsc and tests pass, only the CSS is missing), the recipe for verifying a bulk style change, and pointers to the design system and the tests that enforce the rules."
---

# Panda styling in `website/`

Load this **before** writing or changing any style. The rules below are the ways Panda's
static compilation fails _silently_: the component still renders, the class name is still
emitted, `tsc` and the component tests pass, and only the CSS is wrong or missing. They
have all shipped here before. Structure follows the official Panda agent-skills pattern
(<https://panda-css.com/docs/get-started/agent-skills>): general Panda knowledge lives in
the official skills and docs, this file teaches the project's own rules. The executable
checklist is `website/test/styleConventions.test.ts` (call-site rules) and
`website/test/territoryRule.test.ts` (CSS really generated).

The design system — colours and their jobs, the button recipe, the scales, where styles
live — is in [`website/README.md`](../../../website/README.md); the reasoning behind it is
in [`IDENTITY.md`](../../../website/IDENTITY.md). Read both before adding any style; this
skill is not a summary of them.

## The six silent-failure rules

1. **Never pass a JS variable as a `css({})` value.** Panda resolves values statically at
build time; what it cannot trace without running the code emits no CSS at all. Since the
v2 engine, Panda _does_ trace a local constant and a named import
(`import { accent } from "./palette"`) — but the convention is still the token _name_,
spelled out at the call site: a traced raw value (`"#654321"`) silently goes off-token,
and a value that only exists at runtime (a prop, state, a default or namespace import)
still produces nothing, with no error anywhere.

```ts
css({ color: props.accent }); // ✗ runtime value — silently produces nothing
css({ color: importedHex }); // ✗ v2 emits this, but as a raw literal — off-token
css({ color: "essayAccent" }); // ✓ token name, resolved at build time
```

When a value is needed in both CSS and JS (a Chart.js dataset, an SVG `fill`, an inline
`style={{}}`), define it once as a token in `panda.config.ts`, use the token _name_ inside
`css({})`, and read it via `token("colors.essayAccent")` everywhere else. Pattern:
`components/blog/palette.ts`.

2. **Spacing tokens resolve for single values only, never inside a shorthand.**

```ts
padding: "4" // ✓ var(--spacing-4) = 16px
padding: "2 3" // ✗ emits `2px 3px` — NOT 8px/12px
padding: "8px 12px" // ✓ explicit
paddingY: "2", paddingX: "3" // ✓ longhands do tokenise
```

This makes a bulk raw→token migration shrink every shorthand it touches by ~4×.

3. **Only reference tokens that exist.** An unknown path is passed through as a literal string
and the browser discards the whole declaration — e.g. `token(colors.primary)` when the token
is named `brand` emits `color: colors.primary`. Token names come from `panda.config.ts` and
the generated `styled-system/`, not from guesswork; the official
`panda-css` skill reads that output before writing — do the same.

4. **The `"token(…)"` string form works only inside `css({})`.** Panda resolves it at build
time; a JSX inline style never reaches Panda, so the literal ships and the browser drops
the declaration.

```tsx
style={{ border: "1px solid token(colors.danger)" }} // ✗ no border at all
style={{ border: `1px solid ${token("colors.danger")}` }} // ✓ imported function
className={css({ border: "1px solid token(colors.danger)" })} // ✓ build-time
```

Rule 3 does not catch this — the path is valid, just unresolved.

5. **`fontFamily` must name one of the three site faces** — `reading` (serif prose), `ui`
(sans chrome), `code` (mono), or `inherit`. Two ways to get this wrong, both silent:

```ts
fontFamily: "monospace"; // ✗ CSS generic — bypasses the token system
fontFamily: "mono"; // ✗ Panda's PRESET token — still valid, still the old
// system stack. No error anywhere.
fontFamily: "code"; // ✓
```

The preset's `sans`/`serif`/`mono` survive alongside the custom tokens, so a stale call
site resolves to a real value and simply renders the wrong font. See
[`website/README.md`](../../../website/README.md) → Typography.

6. **A config recipe variant passed as a variable emits only the default.** Rule 1 one level up —
Panda resolves recipe variants statically too.
```tsx
sectionRule({ territory }); // ✗ only defaultVariants gets CSS
sectionRule({ territory: "explore" }); // ✓ literal, extractable
```
The class name is still rendered, so the element gets the recipe's `base` and nothing else.
This shipped: the territory rule was an invisible 48×3px transparent block on all six
`territory="explore"` pages. The fix is not at the call site — list the recipe in `staticCss`:
```ts
staticCss: {
recipes: {
sectionRule: [{ territory: ["*"] }];
}
}
```
Only variants that _paint_ need this. A variant whose style object is empty (`active: { true: {} }`
on the button recipe) styles via `compoundVariants`, which Panda emits as atomic utilities.
`test/territoryRule.test.ts` asserts the CSS is really generated; rule 6 in
`test/styleConventions.test.ts` catches the call site.

## Verifying a bulk style change

`npm run typecheck` and `npm test` cannot see a wrong colour or a dropped declaration. Diff
the _emitted declarations_ instead — build before and after, resolve `var(--…)` back to
literals, and compare. For a pure rename the set must be identical; anything else is either
an intended change or a bug, and the list should be short enough to read.

Note that Panda escapes `.`, `(`, `)` **and commas** in class names
(`.bg-c_rgba\(123\,_63\,_160\,_0\.04\)`), so a naive `grep -F` for an unescaped class reports
false misses.

## Where the general knowledge lives

This file teaches the project. General Panda questions — which primitive to use, theming,
the v1→v2 migration — go to the official skills (`npx skills add chakra-ui/panda`:
`panda-css`, `panda-css-theming`, `panda-css-migrate`) and the docs index at
<https://panda-css.com/docs>.
Loading
Loading