@deslop/coding-standards 0.1.1022 → 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 +11 -10
- package/dist/install.js +1 -1
- package/package.json +1 -1
- package/skills/design/SKILL.md +105 -150
- package/skills/engineering/SKILL.md +38 -42
- package/skills/testing/SKILL.md +21 -22
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
|
|
62
|
-
|
|
|
63
|
-
| [Matt Pocock's engineering skills](https://github.com/mattpocock/skills/tree/d81f3a183412e71a5b1e84ca21bc1a35eea03a60/skills/engineering)
|
|
64
|
-
| [Cursor / pstack](https://github.com/cursor/plugins/tree/e43c7ee26e0038c6c1fa8380dd34ce86ff94cb2a/pstack)
|
|
65
|
-
| [Kit Langton's Effect skill](https://github.com/kitlangton/skills/tree/71195e36024ebd2e66c5beac946e81eafd5abc43/skills/effect)
|
|
66
|
-
| [Vercel's agent skills](https://github.com/vercel-labs/agent-skills/tree/063bee94c3f4df8453406c830b0a7df0f2860278/skills)
|
|
67
|
-
| [Anthropic frontend-design](https://github.com/anthropics/skills/tree/main/skills/frontend-design)
|
|
68
|
-
| [Emil Kowalski's skills](https://github.com/emilkowalski/skills/tree/e8a175de22ae1e49370fc144c1f3bb9aeedf988d/skills)
|
|
69
|
-
| [Dillon Mulroy's skills](https://github.com/dmmulroy/skills/tree/8603380821fee6a77c82639f364ce8fe4f5a92be) and [anti-slop](https://github.com/dmmulroy/anti-slop/tree/c44ef22ca116d0ba62a3ff663a0bd13a3f3fa40b)
|
|
70
|
-
| [
|
|
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.
|
|
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
package/skills/design/SKILL.md
CHANGED
|
@@ -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
|
-
**
|
|
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
|
-
|
|
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
|
-
|
|
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
|
-
|
|
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
|
-
|
|
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
|
-
##
|
|
46
|
-
|
|
47
|
-
These rules cover product UI. A rendered explanation follows the host's rendering contract and these rules.
|
|
24
|
+
## Decide
|
|
48
25
|
|
|
49
|
-
**
|
|
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
|
-
|
|
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
|
-
**
|
|
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
|
-
|
|
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
|
-
**
|
|
34
|
+
**Hierarchy.** Blur a capture: the primary content and the next action still stand out.
|
|
68
35
|
|
|
69
|
-
|
|
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
|
-
|
|
|
72
|
-
|
|
|
73
|
-
|
|
|
74
|
-
|
|
|
75
|
-
|
|
|
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
|
-
|
|
48
|
+
## Build
|
|
78
49
|
|
|
79
|
-
|
|
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
|
-
**
|
|
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
|
-
**
|
|
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
|
-
|
|
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
|
-
**
|
|
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
|
-
-
|
|
105
|
-
-
|
|
106
|
-
-
|
|
107
|
-
-
|
|
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
|
-
|
|
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
|
-
|
|
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
|
-
**
|
|
76
|
+
**Interaction contracts.**
|
|
116
77
|
|
|
117
|
-
|
|
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
|
-
**
|
|
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
|
-
**
|
|
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
|
-
|
|
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
|
-
|
|
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
|
-
**
|
|
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
|
-
|
|
98
|
+
## Prototype and tune
|
|
133
99
|
|
|
134
|
-
**
|
|
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
|
-
|
|
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
|
-
**
|
|
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
|
-
|
|
106
|
+
## Critique before showing
|
|
141
107
|
|
|
142
|
-
|
|
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
|
-
**
|
|
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
|
-
|
|
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
|
-
|
|
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
|
-
**
|
|
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
|
-
|
|
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
|
-
|
|
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
|
|
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
|
-
|
|
|
183
|
-
|
|
|
184
|
-
|
|
|
185
|
-
|
|
186
|
-
|
|
187
|
-
|
|
188
|
-
|
|
189
|
-
- **One
|
|
190
|
-
- **
|
|
191
|
-
- **
|
|
192
|
-
- **
|
|
193
|
-
- **
|
|
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
|
|
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
|
-
|
|
8
|
+
## Simplicity
|
|
9
9
|
|
|
10
|
-
**
|
|
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
|
-
|
|
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
|
-
**
|
|
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
|
-
**
|
|
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
|
-
|
|
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
|
-
**
|
|
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
|
-
**
|
|
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
|
|
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
|
|
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
|
-
**
|
|
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
|
|
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.**
|
|
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
|
-
**
|
|
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
|
-
|
|
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
|
|
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
|
|
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
|
|
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;
|
|
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
|
|
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
|
|
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
|
|
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={<
|
|
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
|
|
597
|
-
- **Pure render.** Derive display values from props and subscribed state during render
|
|
598
|
-
- **State identity.** Keep one owner
|
|
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.
|
package/skills/testing/SKILL.md
CHANGED
|
@@ -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.
|
|
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
|
|
13
|
-
- **Independent.**
|
|
14
|
-
- **Bugs.** Reproduce a bug before
|
|
15
|
-
- **Throwaway.** Settle a
|
|
16
|
-
- **Honest.** No wrong expectations or rule workarounds. Change assertions only for recorded behavior changes
|
|
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.
|
|
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
|
|
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
|
|
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
|
|
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
|
|
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
|
|
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
|
|
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
|
|
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
|
|
60
|
-
- **Inventory.** Collect independent failures
|
|
61
|
-
- **Evidence.**
|
|
62
|
-
- **
|
|
63
|
-
- **
|
|
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.
|