@deslop/coding-standards 0.1.1058 → 0.1.1060

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/README.md CHANGED
@@ -58,16 +58,17 @@ node tools/coding-standards/src/install.ts
58
58
 
59
59
  ### Skill sources
60
60
 
61
- | Source | Used and boundary |
62
- | ---------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
63
- | [Matt Pocock's engineering skills](https://github.com/mattpocock/skills/tree/d81f3a183412e71a5b1e84ca21bc1a35eea03a60/skills/engineering) | Caller-first contracts, causal debugging and one uncertain behavior at a time; no universal TDD |
64
- | [Cursor / pstack](https://github.com/cursor/plugins/tree/e43c7ee26e0038c6c1fa8380dd34ce86ff94cb2a/pstack) | Interface rationale, focused bug replay and defensible benchmarks; no playbook-stage machinery |
65
- | [Kit Langton's Effect skill](https://github.com/kitlangton/skills/tree/71195e36024ebd2e66c5beac946e81eafd5abc43/skills/effect) | Schema construction/optionality, checked against actual [Effect 4 source](https://github.com/Effect-TS/effect/tree/67ba4e46a11ccda0b6761578bfd22c04ae00167d/packages/effect/src) |
66
- | [Vercel's agent skills](https://github.com/vercel-labs/agent-skills/tree/063bee94c3f4df8453406c830b0a7df0f2860278/skills) | Independent I/O and narrow subscriptions, translated to Effect/atoms; no manual React memoization |
67
- | [Anthropic frontend-design](https://github.com/anthropics/skills/tree/main/skills/frontend-design), [Taste Skill](https://github.com/Leonxlnx/taste-skill), [Vercel design guidelines](https://vercel.com/design/guidelines) | Task-led composition and copy in both modes; creative mode adapts Anthropic's studio stance, grounding and generated-default list; no plugin bundle or fixed aesthetic bans |
68
- | [Emil Kowalski's skills](https://github.com/emilkowalski/skills/tree/e8a175de22ae1e49370fc144c1f3bb9aeedf988d/skills) | Realistic layout stress, interruptible motion and mobile checks; no fixed variant picker or audit fan-out. Performance caveats checked against [Motion docs](https://motion.dev/docs/performance) |
69
- | [Dillon Mulroy's skills](https://github.com/dmmulroy/skills/tree/8603380821fee6a77c82639f364ce8fe4f5a92be) and [anti-slop](https://github.com/dmmulroy/anti-slop/tree/c44ef22ca116d0ba62a3ff663a0bd13a3f3fa40b) | Reviewed Effect/error and cleanup approaches; retain our own service layout and ownership |
70
- | [Awesome DESIGN.md](https://github.com/VoltAgent/awesome-design-md) and [Impeccable](https://github.com/pbakaus/impeccable) | Impeccable's concept comparison and screenshot critique shaped creative mode; community brand analyses are inspiration, not authoritative or current tokens |
61
+ | Source | Used and boundary |
62
+ | ---------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
63
+ | [Matt Pocock's engineering skills](https://github.com/mattpocock/skills/tree/d81f3a183412e71a5b1e84ca21bc1a35eea03a60/skills/engineering) | Caller-first contracts, causal debugging and one uncertain behavior at a time; no universal TDD |
64
+ | [Cursor / pstack](https://github.com/cursor/plugins/tree/e43c7ee26e0038c6c1fa8380dd34ce86ff94cb2a/pstack) | Interface rationale, focused bug replay and defensible benchmarks; no playbook-stage machinery |
65
+ | [Kit Langton's Effect skill](https://github.com/kitlangton/skills/tree/71195e36024ebd2e66c5beac946e81eafd5abc43/skills/effect) | Schema construction/optionality, checked against actual [Effect 4 source](https://github.com/Effect-TS/effect/tree/67ba4e46a11ccda0b6761578bfd22c04ae00167d/packages/effect/src) |
66
+ | [Vercel's agent skills](https://github.com/vercel-labs/agent-skills/tree/063bee94c3f4df8453406c830b0a7df0f2860278/skills) | Independent I/O and narrow subscriptions, translated to Effect/atoms; no manual React memoization |
67
+ | [Anthropic frontend-design](https://github.com/anthropics/skills/tree/main/skills/frontend-design) and [Vercel design guidelines](https://vercel.com/design/guidelines) | Task-led composition, copy and interaction contracts (targets, focus, URL state, forms); creative mode keeps grounding and the generated-default list, not a studio persona |
68
+ | [Emil Kowalski's skills](https://github.com/emilkowalski/skills/tree/e8a175de22ae1e49370fc144c1f3bb9aeedf988d/skills) | Realistic layout stress, interruptible motion and mobile checks; no fixed variant picker or audit fan-out. Performance caveats checked against [Motion docs](https://motion.dev/docs/performance) |
69
+ | [Dillon Mulroy's skills](https://github.com/dmmulroy/skills/tree/8603380821fee6a77c82639f364ce8fe4f5a92be) and [anti-slop](https://github.com/dmmulroy/anti-slop/tree/c44ef22ca116d0ba62a3ff663a0bd13a3f3fa40b) | Reviewed Effect/error and cleanup approaches; retain our own service layout and ownership |
70
+ | [Interface Craft](https://www.interfacecraft.dev/library), the user's licensed library | Before-the-first-draft steps, range and depth, agent-run tuning, the four-pass critique, optical, color, type and motion values; ideas adapted in our words, no files copied |
71
+ | [Apple HIG](https://developer.apple.com/design/human-interface-guidelines), [Linear](https://linear.app/now/how-we-redesigned-the-linear-ui), [Rauno Freiberg](https://rauno.me/craft/interaction-design), [Geist](https://vercel.com/geist), [Radix](https://www.radix-ui.com/primitives/docs/overview/accessibility) and [WCAG](https://www.w3.org/WAI/WCAG22/Understanding/contrast-minimum.html) | The quality bar, platform conventions, semantic type and glass, gesture behavior, border and shadow roles, dialog contracts and contrast ratios |
71
72
 
72
73
  These are selectively adapted ideas, not imported frameworks. Concrete behavior, existing stack and settled user decisions govern the maintained skills.
73
74
 
package/dist/install.js CHANGED
@@ -25233,7 +25233,7 @@ const runWith = (command, config) => {
25233
25233
  };
25234
25234
  //#endregion
25235
25235
  //#region package.json
25236
- var version = "0.1.1058";
25236
+ var version = "0.1.1060";
25237
25237
  //#endregion
25238
25238
  //#region src/install.ts
25239
25239
  const cli = pipe(make("deslop-coding-standards", {}, fnUntraced(function* () {
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@deslop/coding-standards",
3
- "version": "0.1.1058",
3
+ "version": "0.1.1060",
4
4
  "repository": {
5
5
  "type": "git",
6
6
  "url": "https://github.com/MP281X/deslop",
@@ -5,197 +5,152 @@ description: 'UI and UX rules for product screens, creative pages and visual exp
5
5
 
6
6
  # Design
7
7
 
8
- Follow the repository's `CODING_STANDARDS.md` and the engineering skill.
8
+ Follow the repository's `CODING_STANDARDS.md` and the engineering skill; where `CODING_STANDARDS.md` sets a style, such as square borders, it wins over this skill.
9
9
 
10
- **Two modes.** Consistent mode works inside an existing product and follows every rule below. Creative mode covers work with no product to match, such as websites, launch pages, portfolios, motion and video. It replaces Decide and Build with [Creative mode](#creative-mode), because dense product rules turn every page into a dashboard; Access and phones and Verify still apply. In a mixed request, the product's screens stay consistent.
10
+ **The bar.** Build what people recommend for its polish and simplicity: Linear, T3 Code, Apple's own apps and apple.com. A person who is not technical uses it without help. The repository's existing screens are the floor, not the target.
11
11
 
12
- ## Decide
13
-
14
- **Direction.** Start from the task, audience, content and the user's settled preferences. The next user action and its content dominate, not a generic "premium" style. When a reference helps, take hierarchy, density and interaction from the accepted one, and say which details you observed and which you inferred. Community brand analyses are inspiration, not verified current tokens.
15
-
16
- **One meaning.** Each visible element adds information, enables a distinct action or shows a relationship. Delete repeated headings, summaries, counts, status badges, decorative wrappers and controls that say or do the same thing, and text that restates the visible example or state. Keyboard shortcuts, screen-reader labels and responsive placement are not redundant; keep their function.
17
-
18
- **Scan first.** The task, its content and the next action read without a paragraph. Use stable visual roles, not another card, badge or icon for each fact. Put detail on demand, but never hide a consequential warning or an essential action.
19
-
20
- **Hierarchy.** Blur a capture: the primary content and the next action still stand out.
21
-
22
- - Unequal priorities get unequal weight; repeat a layout only for records with equal roles.
23
- - Mute labels, metadata and chrome before you enlarge content. Each decision area has one strongest action; the others use secondary or text variants.
24
- - Gaps inside a group are smaller than gaps between groups; space above a heading is larger than below it.
25
- - The first phone viewport shows real content and the next action, not an introduction.
26
- - Left-align prose and records, and keep prose at 45 to 75 characters a line.
27
- - The accent marks actions and selection, status colors mark states, and chart colors mark series.
12
+ **Two modes.** Product mode works inside an app and follows every rule below. Creative mode covers websites, launch pages and motion pieces with no product to match; it adds [Creative mode](#creative-mode) and keeps every other rule.
28
13
 
29
- | Content | Treatment |
30
- | ----------------------------- | ------------------------------------------------------------------------------------- |
31
- | Comparable records or options | Aligned rows and columns with consistent order, units and metadata placement |
32
- | Familiar action | A conventional icon-only control with an accessible name and one owner |
33
- | Ambiguous action | A clear text label, never an invented or guessed icon, or an icon with redundant text |
34
- | Explanation | The real state or example first; text only for missing context or impact |
14
+ ## Before the first draft
35
15
 
36
- ```tsx
37
- // one visible action, named for assistive technology
38
- <Button aria-label="Rename" size="icon"><PencilIcon aria-hidden /></Button>
39
- // redundant label for an already-obvious action
40
- <Button><PencilIcon />Rename</Button>
41
- ```
16
+ The first draft the user sees is already the refined one. Work through these steps privately, then show the result.
42
17
 
43
- Never remove a needed name, status, comfortable hit target or instruction only to make a screen smaller.
18
+ 1. **Task and feeling.** Name the person, their task, their context and the feeling the product gives, such as calm, fast or trustworthy. Name three qualities the person notices, each with what "excellent" looks like.
19
+ 2. **Baseline.** Name the category and platform, and the reference screen for each screen you build: one of Apple's apps on iOS, the product's existing screen, or a reference the user named. Capture the reference in the states you need. Start from the system or established component defaults.
20
+ 3. **Range.** When the structure is open, compare structurally different solutions on the same data, including one that removes the interface by automating the task. Keep the strongest and combine compatible strengths.
21
+ 4. **States.** List every screen, its reachable states (empty, loading, partial, error, success, long content) and the events between them, before any code. Group states by concern instead of multiplying every combination.
22
+ 5. **Settled choices.** Carry forward what the user approved and what he removed. Never re-add a removed element or change an approved detail without a request.
44
23
 
45
- ## Build
46
-
47
- These rules cover product UI. A rendered explanation follows the host's rendering contract and these rules.
24
+ ## Decide
48
25
 
49
- **Components.** Reuse the existing renderer, states and interactions for the same role; read its relevant source or API, not the whole catalogue. Add a missing primitive through the repository's registry command. Fix a wrong shared pattern at its owner when you deliver retained code, not before an exploratory preview. Without a sibling, use the supplied reference and the component defaults within settled preferences.
26
+ **Standard first.** Each screen and interaction follows the convention people know from the reference. Copy its structure, controls, gestures and states. Depart from it only for a deliberate improvement that a capture beside the reference proves.
50
27
 
51
- ```tsx
52
- // same model-selection control used elsewhere
53
- <ModelPicker value={model} onValueChange={setModel} />
54
- // another representation of the same job
55
- <Select value={model} onValueChange={setModel} />
56
- ```
28
+ **Fewest parts.** Start from the content and the next action. Add a control, label, section, color or decoration only when the request, the reference screen or a rule below needs it. When the user calls a screen bloated or wrong, remove parts before you add any.
57
29
 
58
- **Tokens and surfaces.** Use the existing app host, compiler, theme, shared components and Tailwind tokens. Add no escape into custom CSS or dynamic classes and no hardcoded color or size exception, and weaken no shared rule. Keep surfaces flat and dense: group by proximity before borders or elevation, and nest no cards or wrappers that only label content again. Whitespace serves hierarchy, separation or comfortable use, not filling the page.
30
+ **One meaning.** Each visible element adds information, enables a distinct action or shows a relationship. Each action has one control in one place, apart from the gestures and menus the reference repeats. Delete repeated headings, summaries, counts, badges, wrappers and text that restates the visible state. Keyboard shortcuts and screen-reader labels are not repetition.
59
31
 
60
- ```tsx
61
- // existing primitives and semantic tokens
62
- <Empty><EmptyTitle>No runs yet</EmptyTitle><Button onClick={startRun}>Run</Button></Empty>
63
- // parallel hand-built appearance
64
- <div className="rounded-full bg-[#eef] px-[7px] text-[11px]">No runs yet</div>
65
- ```
32
+ **Current work first.** The first viewport shows real content and the next action, not an introduction. Open the current step, diminish completed work without hiding it, and show prerequisites and time expectations where they help.
66
33
 
67
- **Typography.** Give headings, navigation, body text and data deliberate roles from the existing type scale, weights, line heights and line lengths. Use tabular numerals for compared values, and let prose wrap without making controls inconsistent. Keep the established fonts and tokens; never change them for novelty. Never use tiny labels, needless capitals or one accented word in place of hierarchy.
34
+ **Hierarchy.** Blur a capture: the primary content and the next action still stand out.
68
35
 
69
- **Copy.** Name actions after the user's task, not implementation internals, with one vocabulary across controls and confirmations. Use plain verbs and sentence case; labels describe outcomes. An empty state offers the relevant next action. An error names the failed action and the recovery that exists, beside its existing control; never repeat the action in text or invent a recovery that cannot work.
36
+ - Unequal priorities get unequal weight. Mute labels, metadata and chrome before you enlarge content; metadata never looks like user content.
37
+ - Each decision area has one strongest action. Gaps inside a group are smaller than gaps between groups.
38
+ - The accent marks actions and selection, status colors mark states, and chart colors mark series.
39
+ - Put detail on demand, but never hide a consequential warning or an essential action.
70
40
 
71
- | Weak | Useful |
72
- | ---------------------- | ----------------------------------------------------------------- |
73
- | `Submit` | `Save changes`, then `Changes saved` |
74
- | `Something went wrong` | `Couldn't save.` beside the existing retry action; keep the draft |
75
- | `No data` | `No notes yet` beside the existing create action |
41
+ | Content | Treatment |
42
+ | ----------------------------- | ---------------------------------------------------------------------------- |
43
+ | Comparable records or options | Aligned rows and columns with consistent order, units and metadata placement |
44
+ | Familiar action | A conventional icon with an accessible name |
45
+ | Ambiguous action | A clear text label, never a guessed icon |
46
+ | Explanation | The real state or example first; text only for missing context or impact |
76
47
 
77
- **Alignment.** Related headings, search, pinned rows, dividers and records share one grid and inset, and headers share columns with row contents. Action columns stay fixed when labels wrap or are missing. Equivalent controls share component and size: icon box, stroke, hit target, height, baseline and spacing. Pinned content stays outside the scroll area, and borders stay still while scrolling. Fix the owning layout or component, never one instance's offset; equal boxes alone prove neither optical alignment nor legibility. Trace the cause first:
48
+ ## Build
78
49
 
79
- | Defect | Cause | Repair |
80
- | ---------------------------------- | --------------------------------------- | ---------------------------------------------------------------------------------- |
81
- | Squashed icon or avatar | A fixed-size flex child shrinks | `shrink-0` keeps its canonical box |
82
- | Trailing action disappears | The content track refuses to shrink | `min-w-0` on the content and `shrink-0` on the control, or a shrinkable grid track |
83
- | Wrapped rows look adrift | Centering against text of varied height | One deliberate top or baseline alignment |
84
- | Identifiers look identical | Truncation hides the distinct part | Keep that part; show the full value on an existing keyboard and touch surface |
85
- | Counts shift during updates | Proportional digits or mixed formats | `tabular-nums` with consistent locale and units |
86
- | Accents or tall scripts clip | Tight line height or text-box clipping | Fix the owning type scale or overflow; never shrink the font to fit |
87
- | Missing metadata leaves separators | Empty optional slots still render | Omit orphaned separators and lines; reserve space only when layout needs it |
50
+ **Components.** Reuse the existing renderer, states and interactions for the same role, and read its source. Add a missing primitive through the repository's registry command and keep its defaults. Fix a wrong shared pattern at its owner.
88
51
 
89
- **Prevent before rejecting.** When the app already knows an action is not allowed, such as a missing permission, a referenced record or invalid input, disable or hide the control and show the reason beside it. Never let the user act and then show an error the app can predict.
52
+ **Native platforms.** On iOS, build from the system primitives in the current release's style, such as SwiftUI views through `@expo/ui`. They cover navigation, toolbars, search, lists, menus, swipe actions, sheets and SF Symbols. Glass belongs to controls and navigation, never to content. The repository's mobile theme sets colors and type; never draw a custom version of a system control.
90
53
 
91
- **Motion.** Motion explains a change; it never delays input or moves content for decoration. Reuse existing tokens and components: no new library for a fade, and no universal curve, duration, bounce or press scale. Show press, pending, success and failure through the existing primitives.
54
+ **Surfaces.** Use the existing theme, tokens and semantic colors; add no hardcoded color or size. Group by proximity before borders or elevation, and nest no cards. Give each shared edge one border owner, and use translucent border tokens that pick up their background. Build elevation from stacked shadows at 5% black, each with offset equal to blur: 1, 2, 4 and 8 pixels for small elevation. Nested corners are concentric: the inner radius is the outer radius minus the padding between them.
92
55
 
93
- - Use brief existing durations and responsive entry easing. Where no primitive sets the easing, use ease-out for entrances, ease-in-out for morphs and linear only for elapsed progress.
94
- - An entrance starts near its resting size, never from zero. A dismissible surface stays mounted through its exit and reverses its entry path; its panel, backdrop and content move together while unrelated chrome stays still.
95
- - A surface anchored to a trigger enters and exits from it; a centered unanchored modal keeps a centered origin.
96
- - Typing, shortcuts and frequent navigation respond at once, without decorative movement. A rapid toggle or reversal retargets from the visible state, with no input lock, stale-target snap or wait for the earlier transition.
97
- - A drag follows the pointer and keeps the primitive's capture, cancel and release behavior. Reduced motion keeps the feedback with a static or gentle non-spatial equivalent.
98
- - Delay a loader so fast results never flash it, and size it like the final content. List transition properties; never `transition-all`. Delay the first tooltip, then open neighbors at once.
99
- - Transitions suit simple states that retarget; gestures can use the existing spring. Prefer transforms and opacity, but neither CSS nor WAAPI guarantees compositor execution. Profile only an observed performance problem.
100
- - Inspect rapid repetition, reversal and reduced motion in slowed intermediate frames for jumps and stretched text, not only the final frame.
56
+ **Color.** Keep neutrals at one temperature, and derive subtle fills from the main neutral at 10% opacity. Match peer colors by perceived lightness in OKLCH, not HSL; darker shades of a hue gain chroma. Text meets WCAG AA: 4.5:1, or 3:1 for large text. Ease an overlay gradient with 15 smoothstep stops instead of a two-stop fade, and interpolate gradients `in oklch`.
101
57
 
102
- **Access and phones.** The whole task works on a phone, not only stacked desktop panes, and essential actions stay reachable with keyboard, touch and zoom. Use semantic structure, accessible names, visible focus, logical order and deliberate focus return. Inspect long and duplicate content, overflow, contrast, and the failure and permission states that apply. Never remove an accessible affordance for minimalism.
58
+ **Typography.** Use semantic text styles: on iOS the system styles with a 17 point body, on the web the existing scale.
103
59
 
104
- - Hover that stays after a tap needs capability-aware styles; read the framework's handling before you add another gate.
105
- - Inputs use a readable size and the right input type and mode; never disable browser zoom.
106
- - Bottom controls survive real browser chrome, the software keyboard and safe areas, with supported viewport units and insets.
107
- - Scope gesture and selection limits to their owner, and keep scrolling, zoom and selectable content.
108
- - Touch and mouse coexist: never infer capabilities from the user agent or the viewport width. Add no blanket suppression of overscroll, selection or native feedback without a shown need and a working replacement.
109
- - Emulation verifies selected layout cases, not every phone behavior: an affected device-specific keyboard, viewport or gesture contract needs proof on the target device.
60
+ - Use at most five sizes on a screen. Start leading at 1.5 for body, 1.3 for subheadings and 1.1 for headings.
61
+ - Track uppercase labels +0.08em, body 0 and display −0.022em. In dark mode, lower a variable weight by 20. Prose lines hold 45 to 75 characters.
62
+ - Use tabular figures for changing or compared numbers, and right-align numeric columns. Use real quotes, apostrophes, dashes and `…`.
63
+ - Truncate names in the middle when the end tells them apart, such as a file extension.
110
64
 
111
- ## Creative mode
65
+ **Alignment.** Use as few edges and axes as the layout allows. Related headings, search, rows and dividers share one grid and inset, and headers share columns with row contents. Align icons of different shapes by their centers and labels by one edge. Equivalent controls share size, icon style, stroke, radius, fill and hit target. Balance by eye where shapes differ, and encode the offset in the owning component: a button with a trailing arrow starts at 12 pixels left and 9 right. Trace a defect to its cause:
112
66
 
113
- Work as the design lead of a studio whose clients reject anything templated, and aim at an award-winning site, not a tidy product page. Every choice of palette, type, layout and motion comes from this brief.
67
+ | Defect | Cause | Repair |
68
+ | ---------------------------------- | -------------------------------------- | ---------------------------------------------------------------------------------- |
69
+ | Squashed icon or avatar | A fixed-size flex child shrinks | `shrink-0` keeps its canonical box |
70
+ | Trailing action disappears | The content track refuses to shrink | `min-w-0` on the content and `shrink-0` on the control, or a shrinkable grid track |
71
+ | Accessory drifts in a tall row | It follows wrapped text | Center the accessory in the row, independent of the text |
72
+ | Counts shift during updates | Proportional digits or mixed formats | `tabular-nums` with consistent locale and units |
73
+ | Accents or tall scripts clip | Tight line height or text-box clipping | Fix the owning type scale or overflow; never shrink the font to fit |
74
+ | Missing metadata leaves separators | Empty optional slots still render | Omit orphaned separators and lines |
114
75
 
115
- **Ground it.** Before designing, name the subject, the audience and the page's one job. Take the visual language from the subject's world: its materials, tools, documents, places, times of day and vernacular. The brief's data is raw material for an idea, not content to lay out. Copy names concrete capabilities and outcomes, never selling filler.
76
+ **Interaction contracts.**
116
77
 
117
- **Concept first.** Before any system or code, write three concepts that differ in structure, not palette. Each is one paragraph: the big idea in one sentence; the metaphor or artifact from the subject's world; the structure it gives the page, such as a clock from night to morning, a document being read, a journey or one interactive object; the signature moment; the motion; and the visual world of type, color, texture and imagery. Score each from 1 to 5: would a viewer remember it tomorrow, does it belong to this subject alone, does it make the reader act. Discard any concept built as hero, features, list and form. Build the winner whole: the full canvas, display typefaces from Fontshare or Google Fonts chosen for the subject, and canvas or SVG where the idea needs them. Never retreat toward a safe page while building.
78
+ - Hit targets are at least 24 pixels, and 44 on touch. Mobile inputs use 16 pixels or more, the right input type, paste and autofill.
79
+ - Keep focus visible, restore it after a dialog, and close a dialog with Escape. Keep standard keyboard behavior.
80
+ - The URL holds navigation state, and Back restores the scroll position and the draft.
81
+ - Place an error beside its field and move focus to the first one. Undo or confirm a destructive action.
82
+ - When the app already knows an action is not allowed, disable it and show the reason beside it.
118
83
 
119
- **Plan, then challenge it.** Write a compact system: four to six named colors with roles (canvas, ink, atmosphere, action), one or two distinct typefaces with roles and a type scale, and a layout concept with its alignment. Sketch three compositions as ASCII wireframes, not three palettes, and pick one. Then ask what you would produce for any similar brief, change every part of the plan that matches it, and say what changed.
84
+ **States and speed.** A loading state has the layout of the loaded content, and a loader waits before it appears. Use stale-while-revalidate for cached data. Apply writes optimistically and roll back on failure without losing the person's place. Start unavoidable work early, and add no artificial wait. Errors and rare states get the same care as the main path.
120
85
 
121
- **Defaults to avoid** unless the brief asks for one, because they appear whatever the subject and read as generated:
86
+ **Copy.** Name actions after the person's task, with one vocabulary and sentence case. An empty state offers the next action. An error names what failed and the recovery that exists, beside its control.
122
87
 
123
- - a cream canvas with a serif display and a clay accent; near-black with one acid accent; navy ink on pale sage or off-white;
124
- - broadsheet hairlines with zero radius; identical rounded cards with soft shadows and gradient washes;
125
- - all-caps eyebrows, middle-dot meta strings, a monospace label face, `→` on every link, one emphasized headline word, and numbering on content that is no sequence;
126
- - the launch-page skeleton: hero, feature table, ranked list, logo row and email form, each section a heading on the left and a grey paragraph on the right.
88
+ **Motion.** Motion explains a change; it never delays input or decorates.
127
89
 
128
- **Compose.** Build the page around one artifact from the subject's world, shown as it lives there: the message as it arrives, the object in use, the document as it is read. Take the structure from that artifact's story, not a section template, and give the product a mark people remember. Open with the most characteristic thing in its best form: a headline, an image, a live demo or an interaction, not a big number with a gradient by default. Own the grid with deliberate spans, overlaps and whitespace, and repeat a layout only for content with equal roles. Alternate dense demonstrations with quiet reading passages. Typography carries the personality as an active visual element, with body lines under 75 characters. Spend boldness on one signature, keep the rest quiet, and remove one more element before you finish.
90
+ - Frequent actions and typing get no animation. Keep tweens under 300 ms, ease-out for entrances and ease-in-out for morphs. Interface springs start at a 0.4 second duration with 0.15 bounce.
91
+ - An entrance starts near its resting size, from 0.9 scale or more. A surface anchored to a trigger enters and exits from it.
92
+ - Replace content with a crossfade, never exit then entry. Stagger repeated items slightly.
93
+ - A drag follows the finger, keeps velocity on release and resists at its bounds instead of stopping hard. Let people interrupt any motion.
94
+ - Reduced motion keeps the feedback without movement. Inspect slowed intermediate frames, not only the final one.
129
95
 
130
- **Imagery and texture.** Make assets for the subject with one consistent light, material, palette and crop, and keep text and controls in code. Take texture from the subject's real materials, never a universal grain or grid overlay. Never present a generated picture or an invented testimonial as evidence.
96
+ **Access and phones.** The whole task works on a phone, with keyboard, touch and zoom. Text reflows at 200% and with Dynamic Type instead of clipping. Bottom controls clear the keyboard and safe areas. Never remove an accessible affordance for minimalism.
131
97
 
132
- **Motion.** Motion explains a cause and its effect, a before and after, or a process in order; it never only fades content in. Choreograph one moment, such as the page load or one reveal, instead of fade-ups on every section. Before coding a sequence, write its purpose, focal order, movement, easing, duration, overlap and resting moment. Animate interactions from their spatial origin and keep them interruptible. Scroll storytelling follows scroll progress without hijacking it, and the first viewport reads and acts before any animation runs. Every motion has a complete reduced-motion and static version, and offscreen animation stops. A video gets a storyboard of timed keyframes with reading holds, persistent objects and deterministic frames, and its text is checked at playback size.
98
+ ## Prototype and tune
133
99
 
134
- **Critique.** Capture desktop, phone and slowed motion frames, and compare them with the plan and the defaults above. Batch the repairs and recheck once. The quality floor holds without mention: phone layouts, visible keyboard focus, enough contrast and reduced motion.
100
+ **Variants.** When a direction needs the user's judgment, build an interactive render before product code: two or three variants on the same data, each clickable through every state from the States step. Give the page a variant switch and a grouped state selector that the phone can use. Fill it with realistic mock data, and draw it as close as possible to the code you will write: the app's tokens, components, copy and behavior. For a native app the render settles structure, flow and states; settle the final look in simulator captures. Label each variant with its one difference, mark your recommendation, and let the user pick by message.
135
101
 
136
- ## Prototype
102
+ **Real host.** Build one representative screen with realistic data in the real host: the browser preview for the web, the simulator for a native app. An HTML imitation of a native screen misleads.
137
103
 
138
- **Try the question.** Build one rough representative screen or interaction in the existing host with realistic data. Inspect enough to trust the trial, then show it before any merge-readiness work. Label stubs and limits; a sketch is enough only when execution is unnecessary, and it never proves working software. Set a short budget for the first preview, and show a partial result or a blocker rather than growing unseen. Build no production completeness or polished gallery first.
104
+ **Tune.** Settle values yourself; the user never operates a tuning tool. For values you cannot settle by reasoning, such as spacing, timing, springs or blur, capture candidates side by side, including both extremes, and judge them against the reference. Write the chosen values into the source and recapture.
139
105
 
140
- **Use the findings.** For a layout question, compare hierarchy or interaction, not color variants; a style question can compare style. Compare several directions as mockups, and build real attempts only for open uncertainty, feedback or an explicit request. Keep useful comparisons, discard and rebuild freely, and combine compatible strengths, never incompatible mechanisms. Test the weakest assumption while it can still change the choice. A settled direction needs no invented taste question or exploration round.
106
+ ## Critique before showing
141
107
 
142
- ## Verify and show
108
+ Capture the result at the reference's size, theme, data and state, and put it beside the reference. Review it in four separate passes, measure each defect and record the repair:
143
109
 
144
- **Inspect retained UI** in real captures at the sizes the task needs:
110
+ 1. **Structure.** Does the screen open on current work? Is anything redundant, hidden or missing for the task?
111
+ 2. **Behavior.** Do states, focus, errors, gestures and motion behave like the reference? Replay transitions and loop joins.
112
+ 3. **Visual.** Check hierarchy, alignment, spacing, type, color, icon consistency and the generic tells below.
113
+ 4. **Context.** Does it carry the named feeling and the three qualities? Where does a person hesitate?
145
114
 
146
- - Compare related edges, columns, baselines and equivalent controls across rows and screens, not only successful clicks.
147
- - Use long names and URLs, translated labels, Unicode, missing fields and 0, 1 and many records, mixed across rows at the real container width. Respect schema and API limits; never add a validation limit to make a layout pass.
148
- - Check selected, focus, error and scrolling states, and the hierarchy, density, legibility, contrast and clipping.
149
- - Walk the keyboard, touch, narrow, wide, zoom and focus-return journeys that apply, and run axe where it applies; a clean scan proves neither contrast nor usability.
150
- - Inspect video frames in sequence; a playable file is not a good composition.
115
+ Repair in this order: grouping, hierarchy, alignment, copy, then states. Recapture after each batch, and stress long names, Unicode, 0, 1 and many records, the narrow phone and dark mode. Misalignment and inconsistent controls stay defects when types and tests pass.
151
116
 
152
- Batch repairs and recheck only the affected views instead of restarting the audit after each small edit. Misalignment, inconsistent controls and redundant elements stay defects when types and tests pass; fix them before you claim acceptance.
117
+ | Generic tell | Check |
118
+ | ----------------------------------------------- | ----------------------------------------------------- |
119
+ | Every section is a bordered card with an icon | Each container groups something; remove the others |
120
+ | Everything has equal prominence | The blurred capture still shows the primary content |
121
+ | Each tile gets its own accent color | Every hue names a state, series or action |
122
+ | An emphasized headline word or decorative label | Removing the treatment loses no meaning, so remove it |
123
+ | Generic copy such as "seamless" or "Learn more" | Each label names its capability or the outcome |
124
+ | Content that waits for an entrance animation | The first frame is readable and actionable |
153
125
 
154
- **Generic output.** Fix each of these tells before you show a capture:
126
+ **Show the design** with inspected screenshots for layout and a short video for behavior, at consistent sizes, in the user's theme.
155
127
 
156
- | Tell | Check |
157
- | ----------------------------------------------- | ------------------------------------------------------ |
158
- | Every section is a bordered card with an icon | Each container groups something; remove the others |
159
- | Everything has equal prominence | The blurred capture still shows the primary content |
160
- | Uniform gaps | Unrelated groups sit farther apart than related items |
161
- | Each tile gets its own accent color | Every hue names a state, series or action |
162
- | An emphasized headline word or decorative label | Removing the treatment loses no meaning, so remove it |
163
- | Charts, rings or dots without readable data | Each graphic shows values, units or state |
164
- | Generic copy such as "seamless" or "Learn more" | Each label names its capability or the click's outcome |
165
- | Content that waits for an entrance animation | The first frame is readable and actionable |
128
+ ## Creative mode
166
129
 
167
- **Show the design** with inspected screenshots for layout and short video for behavior, at consistent sizes, with the states the decision needs, in the user's theme and the browser's real color preference. Embed existing captures instead of recreating them, and build no variant switcher only to view options. Keep useful media and retire settled attempts; environment decides how long previews run. A review surface opens on the outcome the user judges, with technical detail on demand, and groups by changed behavior, not by file or agent stage. Build no custom dashboard when captures and the pull request answer the question. A design-skill edit is not a product-screen change.
130
+ Ground the page in its subject: name the audience and the page's one job, and take the visual language from the subject's world. Open with the most characteristic thing in its best form, such as a live demo, an image or a headline, and give the product one memorable signature. Interaction, navigation and reading stay conventional; novelty lives in composition, imagery and one choreographed moment. Concept before code: compare three compositions that differ in structure, not palette, and build the winner whole. Avoid defaults that read as generated: a cream canvas with a serif and a clay accent, identical rounded cards with soft shadows, all-caps eyebrows, and the hero, feature table, logo row and email form skeleton.
168
131
 
169
132
  ## Visual explanations
170
133
 
171
- These rules cover diagrams, review pages and rendered agent answers; the pair prompt decides when a reply needs a render. A render adds spatial or interactive information and replaces the text it shows; it never redraws prose, a file list or a fake runtime screenshot.
172
-
173
- | The reader asks | Exhibit |
174
- | ------------------------------------ | -------------------------------------------------------------- |
175
- | What changed on screen? | Before and after pair with the same data, the old state left |
176
- | Which transitions are allowed? | State machine with forbidden edges marked |
177
- | What happens in order, and who acts? | Sequence in swimlanes, one lane per actor |
178
- | Where did the time go? | Timeline with bars on one time axis |
179
- | How much faster, bigger or cheaper? | Bars on a shared axis, values printed on the bars |
180
- | Where exactly is the defect? | Annotated capture with numbered markers and short notes |
181
- | Which requirement has which proof? | Coverage grid of requirements against proof, colored by state |
182
- | Where does the change live? | Change map: a tree with sizes, added and removed parts colored |
183
- | How do the parts relate? | Node and link map with few, labeled links |
184
- | Which option is better? | Options side by side at the same size and with the same data |
185
- | What will a video show? | Storyboard of chapter frames, the outcome frame marked |
186
-
187
- When none fits, choose the form that matches the data's shape, not the first card layout; draw a list as a list.
188
-
189
- - **One message.** Each page answers one question, and its title states the conclusion, such as "The catalog loads 26 times faster", not the topic.
190
- - **One dominant element.** Size, weight and position show importance; mute the context and shrink secondary detail.
191
- - **Color.** Use the theme's status colors only for states and its chart colors only for series. Keep one accent and no decorative color.
192
- - **Labels.** Put names, values and units on the data instead of a distant legend, with one scale per comparison. Repeat small exhibits on one scale rather than crowd one chart.
193
- - **Real content.** Use real names, numbers and captures, and show the scope, so a sample never looks like full coverage.
194
- - **Grid.** Align to one grid with 4, 8, 12 and 16 pixel spacing, two or three type sizes and one corner radius. Labels and captions stay under twelve words, and a visual holds no paragraphs.
195
- - **Check.** Use the host's theme variables, check light, dark and phone width, and fix clipping, overlap, contrast and empty areas before you show it.
196
-
197
- **Avoid** a table or list redrawn as styled boxes, and a card, badge or icon for every fact. Avoid gradients, shadows and illustrations that carry no information, crossing links where an ordered list or lanes show the same relation, and a visual that repeats the text around it.
198
-
199
- ## Image assets
200
-
201
- Generate an image only for a needed illustration, atmosphere, texture or icon exploration, never for text, exact diagrams or fake working UI. Use the harness's native tool; when it is missing, say so instead of inventing an invocation or an API client. Describe the purpose, placement, composition, palette, size and aspect ratio; for an edit, supply the real source and state what stays unchanged. Generate only what the chosen direction needs, and prefer existing icons or CSS when they fit. Inspect the result at its real size for crop, text, edge artifacts, contrast and fit. Keep useful originals, label generated assets as generated, never count them as runtime or browser proof, and keep only used assets in product source.
134
+ These rules cover diagrams, review pages and rendered agent answers; the reply decides when it needs a render. A render adds spatial or interactive information and replaces the text it shows; it never redraws prose, a file list or a fake runtime screenshot.
135
+
136
+ | The reader asks | Exhibit |
137
+ | ------------------------------------ | ------------------------------------------------------------- |
138
+ | What changed on screen? | Before and after pair with the same data, the old state left |
139
+ | Which transitions are allowed? | State machine with forbidden edges marked |
140
+ | What happens in order, and who acts? | Sequence in swimlanes, one lane per actor |
141
+ | Where did the time go? | Timeline with bars on one time axis |
142
+ | How much faster, bigger or cheaper? | Bars on a shared axis, values printed on the bars |
143
+ | Where exactly is the defect? | Annotated capture with numbered markers and short notes |
144
+ | Which requirement has which proof? | Coverage grid of requirements against proof, colored by state |
145
+ | How do the parts relate? | Node and link map with few, labeled links |
146
+ | Which option is better? | Options side by side at the same size and with the same data |
147
+ | What will a video show? | Storyboard of chapter frames, the outcome frame marked |
148
+
149
+ When none fits, choose the form that matches the data's shape; draw a list as a list.
150
+
151
+ - **One message.** Each page answers one question, and its first line states the conclusion, such as "The catalog loads 26 times faster".
152
+ - **One dominant element.** Size, weight and position show importance; mute the context.
153
+ - **Labels.** Put names, values and units on the data, with one scale per comparison. Labels and captions stay under twelve words, and a visual holds no paragraphs.
154
+ - **Real content.** Use real names, numbers and captures, and show the scope.
155
+ - **Interaction.** A render sends nothing back, so decisions come by message. When side-by-side options or captures turn unreadable at phone width, show them at one position behind labeled tabs. The conclusion stays visible without a tap.
156
+ - **Check.** Use the host's theme variables, check light, dark and phone width, and fix clipping, overlap and contrast before you show it.
@@ -3,31 +3,33 @@ name: engineering
3
3
  description: 'TypeScript and Effect code conventions paired with shared lint. Use when designing, implementing or reviewing product and test code, including prototypes.'
4
4
  ---
5
5
 
6
- **Scope.** Apply these rules and repository-specific CODING_STANDARDS.md. Write each line right the first time: the examples exist so lint and review find nothing, and lint is only the fallback.
6
+ **Scope.** Apply these rules and the repository's `CODING_STANDARDS.md`, and write each line right the first time; lint is only the fallback. A refactor or cleanup request applies every rule to every line of the owned files.
7
7
 
8
- **Dependency APIs.** Match the manifest and lockfile versions. Before adding an API wrapper or workaround, read the upstream source per explore's source checkouts. Library APIs do not define coding policy.
8
+ ## Simplicity
9
9
 
10
- **Diagnostics.** Use the shown idiom, not casts, weakened rules or a rewrite trading one diagnostic for another.
10
+ **MVP.** Write the most direct code that meets the request today. Apply YAGNI to every file, function, branch, parameter, option, export, Layer, check, config value, library and abstraction; configure only what callers vary. Write functional, explicit code: pure functions over immutable data, effects only inside Effect, and Effect modules instead of hand-written helpers.
11
11
 
12
- ## Simplicity
12
+ **Plan the shape.** Before writing, choose the file tree, entrypoints and the smallest mechanism per feature, checked against `CODING_STANDARDS.md` and the nearest sibling.
13
+
14
+ **Clarity.** Code, names and files read top to bottom. Rename or split code instead of narrating it; a comment is one line and states only an outside constraint the code cannot show.
15
+
16
+ **Reuse first.** Check existing code, installed modules and platform capabilities before adding code. Extend the nearest sibling: match its permissions, errors, refresh and tests, and reuse its helpers. Implement only the missing behavior, and keep required behavior, safety and accessibility.
17
+
18
+ **Inward dependencies.** Domain and service code never import HTTP, RPC, or other transport types, and domain schemas carry no transport annotation such as an HTTP status.
13
19
 
14
- **Clarity.** Plain, explicit, idiomatic code, names and files; read top to bottom without commentary or cleverness. Match the surrounding code's naming, idiom and comment density.
20
+ **Building blocks.** Design each new public interface from a realistic caller operation and its ordering, failure and lifetime obligations; hide useful complexity, not forwarded names. Offer services, Layers and functions that the caller composes like any Effect module, never a bundle such as `serve(app)`.
15
21
 
16
- **Experiments.** Write with these idioms; reserve the full refactor and cleanup pass for retained delivery. Keep disposable source isolated and shared rules intact.
22
+ **Behavior kept.** A refactor keeps observable behavior at every consumer; printed diagnostics are not a contract. Accept only a difference that no consumer observes or that a rule here causes, such as an error that now keeps its cause, and report each one. Any other behavior change needs the request.
17
23
 
18
- - **Reuse first.** Check existing code, installed modules and platform capabilities before adding code. Implement only the missing behavior, without dropping required behavior, safety or accessibility. Workflow owns the scope. A refactor or cleanup request applies every rule to every line of the owned files.
19
- - **Inward layers.** Domain and service code never import HTTP, RPC, or other transport types, and domain schemas carry no transport annotation such as an HTTP status.
20
- - **Building blocks.** For a consequential new interface, start with a realistic caller operation and its ordering, failure and lifetime obligations; hide useful complexity, not forwarded names. Offer services, Layers and functions the caller composes like any Effect module, never a bundle such as `serve(app)`. No mandatory architecture document or alternatives round.
21
- - **No machinery.** Add hooks, guards or generators only for a required contract or a recurring defect.
22
- - **Behavior kept.** A refactor keeps observable behavior at every consumer. Report accepted differences only when they exist: a change no consumer observes, or one a rule here causes, such as an error keeping its cause. Printed diagnostics are not a contract. Any other behavior change follows workflow's scope.
23
- - **Reachable bugs.** Fix a bug that real input reaches when it lies inside workflow's scope, and check every consumer of the changed output. For a hard bug, use one reproduction and one prediction per probe. Remove edits based on disproven hypotheses, then rerun the original scenario. Delete handling only for input the contract excludes; keep and report code that looks intentional.
24
- - **Performance.** Optimize only when the request or a required contract needs it. Measure one real action on realistic input before and after the change. Measure at the layer that owns the symptom: spans for backend calls, React commits for rerenders, a browser profile for main-thread work. Report run count and variation; a difference within noise is unproved.
25
- - **Type safety.** A fix never weakens types to make a symptom go away: no widened or erased type, cast, dropped generic, or loosened exported type.
26
- - **Domain.** Implement the definition the domain uses, such as a cycle for recursion, never the nearest syntactic proxy.
24
+ **Reachable bugs.** Fix a bug that real input reaches inside the request's scope, and check every consumer of the changed output. For a hard bug, use one reproduction and one hypothesis per probe; remove edits from disproven hypotheses and rerun the original scenario. Delete handling only for input the contract excludes; keep and report code that looks intentional.
27
25
 
28
- **Happy path.** Failures flow through the error channel: no catch, retry, fallback, backup, or defensive check unless the request or an existing contract needs it. Map an expected failure to the service's error; never `Effect.ignore` it or turn it into a defect with `Effect.orDie`.
26
+ **Performance.** Optimize only when the request or a contract needs it. Measure one real action on realistic input before and after, where the symptom shows: spans for backend calls, React commits for rerenders, a browser profile for main-thread work. Report run count and variation; a difference within noise is unproved.
29
27
 
30
- **Trusted data.** Validate and transform once at the boundary with Effect Schema. Carry narrowed values forward. Never re-check what the schema, the declared type, an earlier filter or every caller guarantees.
28
+ **Domain.** Implement the definition the domain uses, such as a cycle for recursion, never the nearest syntactic proxy.
29
+
30
+ **Happy path.** Failures flow through the error channel: no catch, retry, fallback or defensive check unless the request or a contract needs it. Map an expected failure to the service's error; never `Effect.ignore` it or turn it into a defect with `Effect.orDie`.
31
+
32
+ **Trusted data.** Validate and transform once at the boundary with Effect Schema, and carry narrowed values forward. Never re-check what the schema, the declared type, an earlier filter or every caller guarantees.
31
33
 
32
34
  ```ts
33
35
  // good
@@ -39,7 +41,7 @@ Schema.decodeEffect(LedgerDraft)(draft) // draft is already a LedgerDraft
39
41
  variable.init?.type === 'TSSatisfiesExpression' // init was already narrowed to non-null
40
42
  ```
41
43
 
42
- **Effect first.** Search matching cloned Effect modules before hand-writing traversal, state, configuration or platform operations. Replace dependencies covered by Effect or its platform adapters.
44
+ **Effect first.** Search the cloned Effect modules before you hand-write traversal, state, configuration or platform operations. Replace dependencies that Effect or its platform adapters cover.
43
45
 
44
46
  ```ts
45
47
  // good
@@ -53,7 +55,7 @@ Math.floor(Duration.toDays(DateTime.distance(oldest.timestamp, newest.timestamp)
53
55
  import {glob} from 'glob' // a dependency Effect's FileSystem replaces
54
56
  ```
55
57
 
56
- **Whole values.** Pass whole values or spread them. Do not destructure parameters, callbacks or loop bindings, except labeled tuple parameters, React state and component refs. Project fields only to exclude data the recipient must not receive. Name the whole value by its role. A leading underscore marks only a parameter the body never reads; never add an underscore or a number to avoid shadowing.
58
+ **Whole values.** Pass whole values or spread them, named by their role. Do not destructure parameters, callbacks or loop bindings, except labeled tuple parameters, React state and component refs; read other tuples by index. Project fields only to exclude data the recipient must not get. A leading underscore marks only an unread parameter; never add an underscore or a number to avoid shadowing.
57
59
 
58
60
  ```ts
59
61
  // good
@@ -70,9 +72,9 @@ Array.map(Record.toEntries(groups), ([key, entries]) => entries.length)
70
72
  _input => onSubmit(_input.value) // or input2: an outer input is shadowed
71
73
  ```
72
74
 
73
- **Nearest sibling.** Extend the nearest implementation; match permissions, errors, refresh and tests. Reuse its helpers and change only affected state.
75
+ **Dependencies.** Declare a new dependency as `latest`; pin only versions a consumer requires. Use the locked version's API, and read its upstream source before you add a wrapper or workaround. Extend a dependency through its public API; never patch its source. Library APIs do not define coding policy.
74
76
 
75
- **Once.** Extract repeated logic once; one fact has one owner. A domain enum, such as a status, has one schema that every other schema reuses. A cache key or atom family key carries the full identity of what it caches.
77
+ **Once.** Extract repeated logic once; one fact has one owner. A domain enum, such as a status, has one schema that others reuse. A cache or atom family key carries the full identity of what it caches.
76
78
 
77
79
  ```ts
78
80
  // good
@@ -90,17 +92,15 @@ pipe(
90
92
 
91
93
  **Canonical data.** Store and send canonical data, derive display values, expose each state once and remove states no caller produces.
92
94
 
93
- **One way.** Replace superseded paths; no duplicate, subset or superset fields, methods, options or exports.
95
+ **One way.** No duplicate, subset or superset fields, methods, options or exports.
94
96
 
95
97
  **No compatibility.** Rename the shape and every caller together. No alias, fallback, deprecated path or flag unless a documented or user-named contract requires it.
96
98
 
97
- **Present needs.** No speculative option, abstraction, layer, export, file or check. Configure only what callers vary.
98
-
99
- **Dead code.** Every superseded or unused file, export, type, branch, config, validation, doc, test, and dependency goes in the same change. Docs, guides and generated bundles that describe changed behavior change in the same commit.
99
+ **Dead code.** Delete every superseded or unused file, folder, export, type, branch, config, validation, doc, test and dependency in the same change. Update docs and generated bundles that describe changed behavior in the same commit.
100
100
 
101
101
  ## Lint pairs
102
102
 
103
- The forms below are the ones lint enforces without an autofix, each in a form that satisfies every rule involved.
103
+ Lint enforces these forms without an autofix; each example satisfies every rule involved.
104
104
 
105
105
  **Fallback.** A default is `??`; never a ternary on the same value.
106
106
 
@@ -151,7 +151,7 @@ Array.appendAll(
151
151
 
152
152
  **Choices.** `Match` with `Match.exhaustive` for a union, never `switch`.
153
153
 
154
- **Options.** Use Option operators, never .value. Use Option where it composes; keep plain optional values otherwise. This is our convention, not a claim narrowing is unsafe.
154
+ **Options.** Use Option operators, never `.value`. Use Option where it composes; keep plain optional values otherwise.
155
155
 
156
156
  ```ts
157
157
  // good
@@ -164,8 +164,6 @@ const parsed = Option.getOrUndefined(Number.parse(event.target.value))
164
164
 
165
165
  **Handlers.** A handler used once is an arrow in place; a handler used twice is a `function`.
166
166
 
167
- **Pairs.** Read positional tuples by index. Whole values lists the destructuring exceptions.
168
-
169
167
  **Accumulators.** No `let`: a fold or an Effect collection operation that says what it selects.
170
168
 
171
169
  ```ts
@@ -230,7 +228,7 @@ const text = Effect.fn('Ai.text')(function* (response: AiResponse) {
230
228
  })
231
229
  ```
232
230
 
233
- **Casts.** No `as`, `any` or erasing a known shape into `unknown`; `satisfies` and `as const` are the only assertions. Opaque SDK and framework inputs remain `unknown` until narrowed or decoded at their boundary; never fabricate a shape to avoid that type.
231
+ **Casts.** No `as`, `any`, dropped generic, loosened exported type or known shape erased into `unknown`, even to make a symptom go away; `satisfies` and `as const` are the only assertions. Opaque SDK and framework inputs remain `unknown` until narrowed or decoded at their boundary; never fabricate a shape to avoid that type.
234
232
 
235
233
  ```ts
236
234
  // good
@@ -272,7 +270,7 @@ export const EntryKind = Schema.Literals(['income', 'expense']) // no importer
272
270
 
273
271
  **Readonly.** Required on service-shape properties. Allowed on class members and handwritten `Schema.suspend` cycle types; otherwise forbidden.
274
272
 
275
- **Disables.** Fix the idiom or reproduce a rule defect. Inline disables require an unavoidable conflict and -- reason; never disable ordering or weaken file configuration. Report dependency-only diagnostics; rename filename violations.
273
+ **Disables.** Fix a diagnostic with the shown idiom, never a cast, a weakened rule or a rewrite that trades one diagnostic for another, or reproduce a rule defect. Inline disables require an unavoidable conflict and a `--` reason; never disable ordering or weaken file configuration. Report dependency-only diagnostics; rename filename violations.
276
274
 
277
275
  ```ts
278
276
  // good
@@ -368,7 +366,7 @@ static readonly layer = Layer.effect(Ledger, makeLedger) // a make with one call
368
366
  query(sql: string): Effect.Effect<Rows> // a method signature instead of a readonly property
369
367
  ```
370
368
 
371
- **Primitives.** Use Effect's primitives directly; rpcs and services already trace, and one scope owns each lifetime. Compose established independent input and output with Effect.all and explicit appropriate concurrency; keep order-dependent, transactional or resource-conflicting work sequential.
369
+ **Primitives.** Use Effect's primitives directly; RPCs and services already trace, and one scope owns each lifetime. Run independent input and output with Effect.all and explicit concurrency; keep order-dependent, transactional or resource-conflicting work sequential.
372
370
 
373
371
  ```ts
374
372
  // good
@@ -387,7 +385,7 @@ const traced = Effect.withSpan('Notes.create')(notes.create(input))
387
385
  Effect.addFinalizer(() => closeSocket(socket)) // acquisition and release with different owners
388
386
  ```
389
387
 
390
- **Runtime.** The owned runtime runs the program; a Promise an external contract demands is bridged once, where it is returned. React code is no such contract: atoms run its Effects.
388
+ **Runtime.** The owned runtime runs the program. Bridge to a Promise once, where an external contract demands it. React is no such contract: atoms run its Effects.
391
389
 
392
390
  ```ts
393
391
  // good
@@ -451,7 +449,7 @@ function cursorFromBigInt(value: bigint) {
451
449
  payload: Schema.Struct(pipe(PortfolioVisitor.fields, Struct.pick(['color', 'id', 'name', 'x', 'y']))) // the schema itself fits
452
450
  ```
453
451
 
454
- **Construction.** `schema.make` applies defaults and validates synchronously, throwing on failure; use it for trusted construction. For reachable fallible construction with defaults, use `schema.makeEffect` and map its `SchemaIssue.Issue` to the owning domain error. Effect decoders report `SchemaError`; use the correct error contract and do not revalidate already-narrowed data.
452
+ **Construction.** `schema.make` applies defaults and validates synchronously, throwing on failure: use it for trusted construction. For reachable fallible construction, use `schema.makeEffect` and map its `SchemaIssue.Issue` to the owning domain error. Effect decoders report `SchemaError`.
455
453
 
456
454
  ```ts
457
455
  // good — reachable constructor failure stays in the error channel
@@ -495,7 +493,7 @@ function isPathNotFound(error: unknown) {
495
493
 
496
494
  ## Shape
497
495
 
498
- **Inline.** Inline forwarding wrappers and single-use helpers, temporaries and constants. Argument-free functions are named values.
496
+ **Inline.** Inline forwarding wrappers, temporaries and constants. Keep a helper only for logic that is reused or deserves its own name. Argument-free functions are named values.
499
497
 
500
498
  ```ts
501
499
  // good
@@ -574,7 +572,7 @@ onClick={() => Effect.runPromise(saveApp(draft))}
574
572
  useAtomValue(Atom.make(loadApp(appId))) // a new atom on every render
575
573
  ```
576
574
 
577
- **Outcomes.** Read data with `useAtomSuspense`. The route or section that owns a screen wraps it in one `Suspense` boundary for loading. One error boundary there shows the failure with a retry. Components never branch on loading or failure, and never turn a failure into an empty value. Take a write's pending and failure state from its atom's result, never from copied React state.
575
+ **Outcomes.** Read data with `useAtomSuspense`. The route or section that owns a screen wraps it in one `Suspense` boundary for loading. One error boundary there shows the failure with a retry. Components never branch on loading or failure, and never turn a failure into an empty value. Take a write's pending and failure state from its atom's result, never from copied React state. A reversible write updates the screen at once through `Atom.optimistic` and `Atom.optimisticFn`, which roll back on failure.
578
576
 
579
577
  ```tsx
580
578
  // good
@@ -582,7 +580,7 @@ function AppFrame(props: {appId: AppId}) {
582
580
  return <Frame app={useAtomSuspense(appAtom(props.appId)).value} />
583
581
  }
584
582
  <ErrorBoundary fallback={<LoadError onRetry={refresh} />}>
585
- <Suspense fallback={<Spinner />}>
583
+ <Suspense fallback={<AppFrameSkeleton />}>
586
584
  <AppFrame appId={appId} />
587
585
  </Suspense>
588
586
  </ErrorBoundary>
@@ -593,8 +591,6 @@ const members = query.data ?? [] // a failed load looks like no members
593
591
  const [pending, setPending] = useState(false) // copies the write atom's waiting state
594
592
  ```
595
593
 
596
- - **Ownership.** Give a component that owns its own atoms, writes or lifetime its own file; keep small presentational helpers beside their one user. Reset an editable draft through a `key` on its identity, never an Effect that copies props.
597
- - **Pure render.** Derive display values from props and subscribed state during render; never mutate them or perform an RPC, write or subscription there. Event handlers own user-triggered actions; Effects synchronize external systems, not copied state or calculations. Reuse the existing router and atom integration rather than fetching in a new Effect.
598
- - **State identity.** Keep one owner for each value, stable domain keys for list items and deliberate reset boundaries. For an observed rerender problem, derive the needed value in the existing atom graph instead of subscribing to frequently changing unrelated state. Compiler memoization is a performance optimization, never a correctness or lifetime guarantee. Do not add manual memoization, compiler escape directives or weakened hook rules to hide an ownership problem.
599
-
600
- Testing judgment, fixtures, assertions and verification belong to the testing skill; these engineering rules still apply to test code.
594
+ - **Ownership.** Give a component that owns its own atoms, writes or lifetime its own file; keep small presentational helpers beside their one user. Reset an editable draft through a `key` on its identity, never a `useEffect` that copies props.
595
+ - **Pure render.** Derive display values from props and subscribed state during render, and never mutate them or run an RPC, write or subscription there. Event handlers own user actions; `useEffect` synchronizes only external systems, never copied state or calculations.
596
+ - **State identity.** Keep one owner per value, stable domain keys for list items and deliberate reset boundaries. For an observed rerender problem, derive the value in the existing atom graph instead of subscribing to unrelated changing state. Compiler memoization never guarantees correctness or lifetime; add no manual memoization, compiler escape directives or weakened hook rules to hide an ownership problem.
@@ -7,15 +7,15 @@ description: 'Select and run black-box behavior proof for Effect apps and tools.
7
7
 
8
8
  **Scope.** Test behavior the changed code owns; a coverage count proves nothing.
9
9
 
10
- - **Worth it.** Test owned branching, computation, parsing and state transitions that a plausible regression breaks. Add missing valuable coverage before refactoring.
11
- - **Public interface.** Test exported Layers, services and functions through observable behavior, so a rewrite that keeps the contract keeps the tests green. The browser proves UI components.
12
- - **Redundant.** Delete touched cases that only repeat dependency guarantees, constants, wording, removed behavior or an already covered input. Keep boundary tests that prove the application's own validation, encoding or integration.
13
- - **Independent.** Expected values come from the contract or a worked example, not from recomputing the implementation. Use the smallest reachable fixture that tells the cases apart.
14
- - **Bugs.** Reproduce a bug before fixing it. Keep the failing case when it protects owned behavior, in the existing test that covers the fixed code, or a new case when none fits.
15
- - **Throwaway.** Settle a worry with a throwaway test or prototype and delete it once answered, unless it becomes a regression test under Bugs.
16
- - **Honest.** No wrong expectations or rule workarounds. Change assertions only for recorded behavior changes; preserve every still-relevant value.
10
+ - **Worth it.** Test owned branching, computation, parsing and state transitions that a plausible regression breaks.
11
+ - **Public interface.** Test exported Layers, services and functions through observable behavior, so a rewrite that keeps the contract keeps the tests green. The browser proves web UI components; the simulator proves native ones.
12
+ - **Redundant.** Delete touched cases that only repeat dependency guarantees, constants, wording, removed behavior or a covered input. Keep boundary tests of the application's own validation, encoding or integration.
13
+ - **Independent.** Take expected values from the contract or a worked example, never from the implementation. Use the smallest reachable fixture that tells the cases apart.
14
+ - **Bugs.** Reproduce a bug before you fix it. Keep the failing case when it protects owned behavior, in the existing test of the fixed code, or in a new case when none fits.
15
+ - **Throwaway.** Settle a question with a spike, and delete it once answered unless it becomes a regression test.
16
+ - **Honest.** No wrong expectations or rule workarounds. Change assertions only for recorded behavior changes, and keep every still-relevant value.
17
17
 
18
- **Assertions.** Use one representative input per behavior, in its existing case. Share a helper only between cases that use it. Use @effect/vitest assertions for outputs and structured error identity. Replace dependencies through Layers or public parameters, never global or module mocks. Isolate mutable state in its owning fixture.
18
+ **Assertions.** Use one representative input per behavior, in its existing case. Share a helper only between cases that use it. Use @effect/vitest assertions for outputs and structured error identity. Replace dependencies through Layers or public parameters, never global or module mocks. Keep mutable state in its owning fixture.
19
19
 
20
20
  ```ts
21
21
  // good — "test files only for the services/packages public interfaces"
@@ -38,27 +38,26 @@ assert.strictEqual(Array.flatMap(attioProvider.groups, group => group.actions).l
38
38
 
39
39
  ## Choose cases
40
40
 
41
- - **Contract.** Connect a reachable input to an observable output, so a plausible wrong implementation fails. Use the nearest public interface, not private helpers or new harnesses. When behavior is uncertain, settle one reachable case with a focused check and a minimal implementation. Then choose the next case from the findings; never batch tests for an imagined implementation or require universal TDD.
41
+ - **Contract.** Connect a reachable input to an observable output, so a plausible wrong implementation fails. Use no new harnesses. When behavior is uncertain, settle one reachable case with a focused check and a minimal implementation, then choose the next case from the findings. Never batch tests for an imagined implementation or require universal TDD.
42
42
  - **Branches.** Exercise the reachable distinctions: empty, duplicate, concurrent and dependency failure. Write no Cartesian product, and no random or repeated happy paths without a hypothesis.
43
43
  - **Failure.** Use `Effect.flip` for an expected failure and `Effect.exit` for both outcomes. Assert domain identity and preserved state and resources: no partial write, lost value, extra retry or leak. Wording or rejection alone is not enough, and incidental order is no assertion.
44
- - **Control.** Run the regression test against the unfixed implementation, and confirm that the intended assertion fails, not setup, imports or compilation. Before a refactor, cover the old behavior first.
44
+ - **Control.** Run the regression test against the unfixed implementation, and confirm that the intended assertion fails, not setup, imports or compilation. Before a refactor, cover the old behavior.
45
45
 
46
46
  ## Effect and fixtures
47
47
 
48
- - **Entrypoint.** Plain `it` tests sync and Promise public functions. `it.effect(name, () => effect)` runs with virtual services and `it.live` with real ones. Return the Effect from the callback, with no runner wrapper. `it.layer` caches its block context and exposes `effect`, not `live`; shared live layers use `{excludeTestServices: true}`. Check APIs and imports against the cloned source.
48
+ - **Entrypoint.** Plain `it` tests sync and Promise functions. `it.effect(name, () => effect)` runs with virtual services and `it.live` with real ones; return the Effect, with no runner wrapper. `it.layer` caches its block context and exposes `effect`, not `live`; shared live layers use `{excludeTestServices: true}`. Check APIs and imports against the cloned source.
49
49
  - **Lifetime.** `it.effect` and `it.live` supply a Scope per case; `it.layer` acquisition belongs to its block. Use scoped platform resources and no redundant whole-case `Effect.scoped`. To assert cleanup, close the smaller operation scope before you inspect.
50
- - **Isolation.** Shared layers share mutable services, `TestClock` and `TestConsole`, and `Layer.fresh` does not reset cached context. Use distinct keys, a public reset or case-local fixtures. Cases pass alone and together, and never depend on earlier output.
51
- - **Clock.** Take `TestClock` and `TestConsole` from `effect/testing`. Fork, await a `Deferred` at the dependency boundary that carries the actual key or path, adjust time, then join; forking alone is not readiness. JavaScript timers, sockets and processes need live services, not `TestClock`.
52
- - **Concurrency.** Give the dependency an entered and release pair: await entry, start the competing action, release, then assert the contract. Set `Effect.all` concurrency for overlap. For cancellation, await acquisition, interrupt and await completion; an unrelated outer Scope stays open. Never test primitives or incidental ordering.
53
- - **Boundary.** Double only dependencies outside the tested logic, through a Layer or a public parameter. A claim about an SDK, process, filesystem, HTTP, browser or package integration needs the real boundary, not matching mocks or spies.
50
+ - **Isolation.** Shared layers share mutable services, `TestClock` and `TestConsole`, and `Layer.fresh` does not reset cached context. Use distinct keys, a public reset or case-local fixtures. Cases pass alone and together.
51
+ - **Clock.** Take `TestClock` and `TestConsole` from `effect/testing`. Fork, await a `Deferred` at the dependency boundary that carries the actual key or path, adjust time, then join; forking alone is not readiness. JavaScript timers, sockets and processes need live services.
52
+ - **Concurrency.** Give the dependency an entered and release pair: await entry, start the competing action, release, then assert the contract. Set `Effect.all` concurrency for overlap. For cancellation, await acquisition, interrupt and await completion, and assert that an unrelated outer Scope stays open. Never test primitives or incidental ordering.
53
+ - **Boundary.** Double only dependencies outside the tested logic, through a Layer or a public parameter. A claim about an SDK, process, filesystem, HTTP, browser or package integration needs the real boundary, not mocks or spies.
54
54
  - **Artifacts.** Prove a public tool with real packing and declared dependencies, not source imports or stubs. Generate fixtures with the real generator after one representative input settles; never alter expected output to fit a bug.
55
- - **Platform.** `FileSystem.layerNoop` is not memory storage, and `HttpServer.layerServices` supplies no filesystem; use a platform Layer and scoped paths. `RpcTest` bypasses serialization, and `HttpApiTest` bypasses sockets while it tests routing, encoding, middleware and decoding. Neither proves deployment or an external integration.
55
+ - **Platform.** `FileSystem.layerNoop` is not memory storage, and `HttpServer.layerServices` supplies no filesystem: use a platform Layer and scoped paths. `RpcTest` bypasses serialization, and `HttpApiTest` bypasses sockets but proves routing, encoding, middleware and decoding; neither proves deployment or an external integration.
56
56
 
57
57
  ## Run and reconcile
58
58
 
59
- - **Run.** Use the documented test selectors and confirm that they collect and execute the intended cases. Zero collected tests, setup alone or exit code 0 alone prove nothing.
60
- - **Inventory.** Collect independent failures once instead of cancelling the suite at the first failure. Distinguish separate cases from repeated observations. Rerun a timed-out test once in isolation; a pass proves that run, not the cause of the timeout. Never call a bypassed selector or a skipped operation flaky.
61
- - **Evidence.** Preserve the first failure, exact command, execution count and exit status. Separate cause from hypothesis, product defect from setup failure, and a skipped criterion from a passing one. A source read or overall green command never substitutes for a missing behavior check.
62
- - **Stable inputs.** A check proves only the source, dependencies and configuration it ran on; an edit to them invalidates its result.
63
- - **Final proof.** Reconcile every required behavior on the final code. Screenshots and video prove visible outcomes; execution through the public interface proves the rest.
64
- - **Visual proof.** A capture proves one claim and ends on its outcome, held long enough to read: the run, the file or the rejection. Seed data first, and keep loading states off camera unless loading is the claim. Show realistic journeys end to end, cropped to the element at a readable scale; speed up waits instead of cutting steps. A capture is stale once any component it shows changes.
59
+ - **Run.** Use the documented test selectors, and confirm that they collect and run the intended cases. Zero collected tests, setup alone or exit code 0 alone prove nothing.
60
+ - **Inventory.** Collect independent failures in one run instead of stopping at the first, and tell separate cases from repeated observations. Rerun a timed-out test once in isolation; a pass proves that run, not the cause. Never call a bypassed selector or a skipped operation flaky.
61
+ - **Evidence.** Keep the first failure, exact command, execution count and exit status. Separate cause from hypothesis, product defect from setup failure, and a skipped criterion from a passing one. A source read or a green command never replaces a missing behavior check.
62
+ - **Final proof.** A check proves only the source, dependencies and configuration it ran on, so reconcile every required behavior on the final code. Screenshots and video prove visible outcomes; execution through the public interface proves the rest.
63
+ - **Visual proof.** A capture proves one claim and ends on its outcome, held long enough to read: the run, the file or the rejection. Seed data first, and keep loading states off camera unless loading is the claim. Show realistic journeys end to end, cropped to the element at a readable scale; speed up waits instead of cutting steps. A capture is stale once a component it shows changes.