@deslop/coding-standards 0.1.909 → 0.1.913
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/dist/install.js +1 -1
- package/package.json +1 -1
- package/skills/design/SKILL.md +100 -99
- package/skills/engineering/SKILL.md +17 -17
- package/skills/testing/SKILL.md +15 -15
package/dist/install.js
CHANGED
|
@@ -24685,7 +24685,7 @@ const runWith = (command, config) => {
|
|
|
24685
24685
|
};
|
|
24686
24686
|
//#endregion
|
|
24687
24687
|
//#region package.json
|
|
24688
|
-
var version = "0.1.
|
|
24688
|
+
var version = "0.1.913";
|
|
24689
24689
|
//#endregion
|
|
24690
24690
|
//#region src/install.ts
|
|
24691
24691
|
const cli = pipe(make("deslop-coding-standards", {}, fnUntraced(function* () {
|
package/package.json
CHANGED
package/skills/design/SKILL.md
CHANGED
|
@@ -1,30 +1,37 @@
|
|
|
1
1
|
---
|
|
2
2
|
name: design
|
|
3
|
-
description: 'UI and UX rules for screens and visual explanations. Use when building, prototyping
|
|
3
|
+
description: 'UI and UX rules for product screens, creative pages and visual explanations. Use when building, prototyping or reviewing a component, page, diagram, review page or other rendered agent output.'
|
|
4
4
|
---
|
|
5
5
|
|
|
6
6
|
# Design
|
|
7
7
|
|
|
8
|
-
|
|
8
|
+
Follow the repository's `CODING_STANDARDS.md` and the engineering skill.
|
|
9
9
|
|
|
10
|
-
**Two modes.** Consistent mode works inside an existing product
|
|
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.
|
|
11
11
|
|
|
12
12
|
## Decide
|
|
13
13
|
|
|
14
|
-
**Direction.** Start from the
|
|
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
15
|
|
|
16
|
-
**One meaning.** Each visible element
|
|
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
17
|
|
|
18
|
-
**Scan
|
|
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
19
|
|
|
20
|
-
**Hierarchy.** Blur a capture: the primary content and the next action
|
|
20
|
+
**Hierarchy.** Blur a capture: the primary content and the next action still stand out.
|
|
21
21
|
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
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.
|
|
28
|
+
|
|
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 |
|
|
28
35
|
|
|
29
36
|
```tsx
|
|
30
37
|
// one visible action, named for assistive technology
|
|
@@ -33,13 +40,13 @@ description: 'UI and UX rules for screens and visual explanations. Use when buil
|
|
|
33
40
|
<Button><PencilIcon />Rename</Button>
|
|
34
41
|
```
|
|
35
42
|
|
|
36
|
-
|
|
43
|
+
Never remove a needed name, status, comfortable hit target or instruction only to make a screen smaller.
|
|
37
44
|
|
|
38
45
|
## Build
|
|
39
46
|
|
|
40
|
-
These rules
|
|
47
|
+
These rules cover product UI. A rendered explanation follows the host's rendering contract and these rules.
|
|
41
48
|
|
|
42
|
-
**
|
|
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.
|
|
43
50
|
|
|
44
51
|
```tsx
|
|
45
52
|
// same model-selection control used elsewhere
|
|
@@ -48,7 +55,7 @@ These rules apply to product UI. A rendered explanation follows the host's rende
|
|
|
48
55
|
<Select value={model} onValueChange={setModel} />
|
|
49
56
|
```
|
|
50
57
|
|
|
51
|
-
**Tokens and
|
|
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.
|
|
52
59
|
|
|
53
60
|
```tsx
|
|
54
61
|
// existing primitives and semantic tokens
|
|
@@ -57,105 +64,111 @@ These rules apply to product UI. A rendered explanation follows the host's rende
|
|
|
57
64
|
<div className="rounded-full bg-[#eef] px-[7px] text-[11px]">No runs yet</div>
|
|
58
65
|
```
|
|
59
66
|
|
|
60
|
-
**Typography.** Give headings, navigation, body text and data deliberate roles
|
|
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.
|
|
61
68
|
|
|
62
|
-
**Copy.** Name actions
|
|
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.
|
|
63
70
|
|
|
64
71
|
| Weak | Useful |
|
|
65
72
|
| ---------------------- | ----------------------------------------------------------------- |
|
|
66
|
-
| `Submit` | `Save changes
|
|
73
|
+
| `Submit` | `Save changes`, then `Changes saved` |
|
|
67
74
|
| `Something went wrong` | `Couldn't save.` beside the existing retry action; keep the draft |
|
|
68
75
|
| `No data` | `No notes yet` beside the existing create action |
|
|
69
76
|
|
|
70
|
-
**Alignment.**
|
|
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:
|
|
71
78
|
|
|
72
|
-
| Defect |
|
|
73
|
-
| ---------------------------------- |
|
|
74
|
-
| Squashed icon
|
|
75
|
-
| Trailing action disappears |
|
|
76
|
-
| Wrapped rows look adrift |
|
|
77
|
-
| Identifiers look identical | Truncation hides the
|
|
78
|
-
| Counts shift during updates | Proportional digits
|
|
79
|
-
| Accents
|
|
80
|
-
| Missing metadata leaves separators | Empty optional slots still render
|
|
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 |
|
|
81
88
|
|
|
82
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.
|
|
83
90
|
|
|
84
|
-
**Motion.**
|
|
85
|
-
|
|
86
|
-
| Interaction | Treatment |
|
|
87
|
-
| -------------------------------------- | -------------------------------------------------------------------------------------------------- |
|
|
88
|
-
| Typing, shortcuts, frequent navigation | Immediate response; no decorative movement |
|
|
89
|
-
| Trigger-anchored surface | Enter/exit from its trigger; a centered unanchored modal keeps a centered origin |
|
|
90
|
-
| Rapid toggle/reversal | Retarget from the visible state; no input lock, stale-target snap or wait for the prior transition |
|
|
91
|
-
| Drag/gesture | Follow the pointer; preserve the existing primitive's capture, cancellation and release behavior |
|
|
92
|
-
| Reduced motion | Preserve feedback through a suitable static or gentle non-spatial equivalent |
|
|
93
|
-
|
|
94
|
-
Use responsive entry easing and brief existing durations. Simple retargetable states suit transitions; gestures can use the existing spring. Prefer transforms and opacity where appropriate, but CSS and WAAPI do not guarantee compositor execution. Profile observed performance problems, not every animation. Inspect rapid repetition, reversal and reduced motion, not only the final frame.
|
|
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.
|
|
95
92
|
|
|
96
|
-
|
|
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.
|
|
97
101
|
|
|
98
|
-
|
|
99
|
-
| --------------------------------------------- | ---------------------------------------------------------------------------------------------------- |
|
|
100
|
-
| Hover remains after tap | Capability-aware styles; inspect the framework's handling before adding another gate |
|
|
101
|
-
| Input zoom/wrong keyboard | Readable input sizing + correct input type/mode; never disable browser zoom |
|
|
102
|
-
| Bottom controls disappear | Real browser chrome, software keyboard and safe areas; appropriate supported viewport sizing/insets |
|
|
103
|
-
| Drag blocks scrolling/content can't be copied | Scope gesture/selection restrictions to their owner; preserve scrolling, zoom and selectable content |
|
|
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.
|
|
104
103
|
|
|
105
|
-
|
|
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.
|
|
106
110
|
|
|
107
111
|
## Creative mode
|
|
108
112
|
|
|
109
|
-
Work as the design lead of a studio whose clients reject anything templated, and aim at
|
|
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.
|
|
110
114
|
|
|
111
|
-
**Ground it.**
|
|
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.
|
|
112
116
|
|
|
113
|
-
**Concept first.** Before any system or code, write three concepts that differ in structure, not
|
|
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.
|
|
114
118
|
|
|
115
|
-
**Plan, then challenge
|
|
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.
|
|
116
120
|
|
|
117
|
-
**Defaults to avoid
|
|
121
|
+
**Defaults to avoid** unless the brief asks for one, because they appear whatever the subject and read as generated:
|
|
118
122
|
|
|
119
|
-
|
|
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.
|
|
120
127
|
|
|
121
|
-
**
|
|
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.
|
|
122
129
|
|
|
123
|
-
**
|
|
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.
|
|
124
131
|
|
|
125
|
-
**
|
|
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.
|
|
133
|
+
|
|
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.
|
|
126
135
|
|
|
127
136
|
## Prototype
|
|
128
137
|
|
|
129
|
-
**Try the question.** Build one rough representative screen or interaction in the existing host with realistic data. Inspect enough to
|
|
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.
|
|
130
139
|
|
|
131
|
-
**Use the findings.** For layout
|
|
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.
|
|
132
141
|
|
|
133
142
|
## Verify and show
|
|
134
143
|
|
|
135
|
-
**Inspect retained UI
|
|
144
|
+
**Inspect retained UI** in real captures at the sizes the task needs:
|
|
136
145
|
|
|
137
|
-
|
|
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.
|
|
138
151
|
|
|
139
|
-
|
|
140
|
-
| ----------------------------------------------- | -------------------------------------------------------- |
|
|
141
|
-
| Every section is a bordered card with an icon | Each container has a grouping purpose; remove the others |
|
|
142
|
-
| Everything has equal prominence | The blurred capture still shows the primary content |
|
|
143
|
-
| Uniform gaps | Unrelated groups sit farther apart than related items |
|
|
144
|
-
| Each tile gets its own accent color | Every hue names a state, series or action |
|
|
145
|
-
| An emphasized headline word or decorative label | Removing the treatment loses no meaning, so remove it |
|
|
146
|
-
| Charts, rings or dots without readable data | Each graphic shows values, units or state |
|
|
147
|
-
| Generic copy such as "seamless" or "Learn more" | Each label names its capability or the click's outcome |
|
|
148
|
-
| Content that waits for an entrance animation | The first frame is readable and actionable |
|
|
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.
|
|
149
153
|
|
|
150
|
-
**
|
|
154
|
+
**Generic output.** Fix each of these tells before you show a capture:
|
|
151
155
|
|
|
152
|
-
|
|
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 |
|
|
153
166
|
|
|
154
|
-
|
|
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.
|
|
155
168
|
|
|
156
|
-
|
|
169
|
+
## Visual explanations
|
|
157
170
|
|
|
158
|
-
|
|
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.
|
|
159
172
|
|
|
160
173
|
| The reader asks | Exhibit |
|
|
161
174
|
| ------------------------------------ | -------------------------------------------------------------- |
|
|
@@ -171,30 +184,18 @@ These rules cover diagrams, review pages and rendered agent answers; the pair pr
|
|
|
171
184
|
| Which option is better? | Options side by side at the same size and with the same data |
|
|
172
185
|
| What will a video show? | Storyboard of chapter frames, the outcome frame marked |
|
|
173
186
|
|
|
174
|
-
When none fits, choose the form that matches the data's shape, not the first card layout
|
|
175
|
-
|
|
176
|
-
- **One message.** Each page answers one question. Its title states the conclusion, such as "The catalog loads 26 times faster", not the topic.
|
|
177
|
-
- **One dominant element.** Size, weight and position show importance. Mute the context and make secondary detail smaller.
|
|
178
|
-
- **Color means something.** Use the theme's status colors only for states and the chart colors only for series. Keep one accent, and never color for decoration.
|
|
179
|
-
- **Label directly.** Put names and values on the data instead of a distant legend. Show units, and use one scale per comparison.
|
|
180
|
-
- **Real content.** Use real names, numbers and captures. Show the scope, so a sample never looks like full coverage.
|
|
187
|
+
When none fits, choose the form that matches the data's shape, not the first card layout; draw a list as a list.
|
|
181
188
|
|
|
182
|
-
- **
|
|
183
|
-
- **
|
|
184
|
-
- **
|
|
185
|
-
- **
|
|
186
|
-
- **
|
|
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.
|
|
187
196
|
|
|
188
|
-
**Avoid**
|
|
189
|
-
|
|
190
|
-
- A table or bullet list redrawn as styled boxes.
|
|
191
|
-
- A card, badge or icon for every fact.
|
|
192
|
-
- Gradients, shadows and illustrations that carry no information.
|
|
193
|
-
- Crossing links, when an ordered list or lanes would show the same relation.
|
|
194
|
-
- A visual that repeats the text around it.
|
|
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.
|
|
195
198
|
|
|
196
199
|
## Image assets
|
|
197
200
|
|
|
198
|
-
|
|
199
|
-
|
|
200
|
-
Describe purpose, placement, composition, palette and intended size and aspect ratio. For edits, supply the real source and state what remains unchanged. Generate only what the chosen direction needs; prefer existing icons or CSS when they express the job precisely. Inspect the returned asset at its intended size for crop, text and edge artifacts, contrast and fit. Preserve useful originals and identify generated assets as such; they are not runtime or browser proof. Keep only used assets in product source.
|
|
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.
|
|
@@ -1,11 +1,11 @@
|
|
|
1
1
|
---
|
|
2
2
|
name: engineering
|
|
3
|
-
description: 'TypeScript and Effect code conventions paired with shared lint. Use when designing, implementing or reviewing product
|
|
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
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.
|
|
7
7
|
|
|
8
|
-
**Dependency APIs.** Match the manifest and lockfile versions. Before adding an API wrapper or workaround, read the upstream source per
|
|
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.
|
|
9
9
|
|
|
10
10
|
**Diagnostics.** Use the shown idiom, not casts, weakened rules or a rewrite trading one diagnostic for another.
|
|
11
11
|
|
|
@@ -13,7 +13,7 @@ description: 'TypeScript and Effect code conventions paired with shared lint. Us
|
|
|
13
13
|
|
|
14
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.
|
|
15
15
|
|
|
16
|
-
**Experiments.** Write with these idioms; reserve the full refactor
|
|
16
|
+
**Experiments.** Write with these idioms; reserve the full refactor and cleanup pass for retained delivery. Keep disposable source isolated and shared rules intact.
|
|
17
17
|
|
|
18
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
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.
|
|
@@ -21,7 +21,7 @@ description: 'TypeScript and Effect code conventions paired with shared lint. Us
|
|
|
21
21
|
- **No machinery.** Add hooks, guards or generators only for a required contract or a recurring defect.
|
|
22
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
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
|
|
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
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
26
|
- **Domain.** Implement the definition the domain uses, such as a cycle for recursion, never the nearest syntactic proxy.
|
|
27
27
|
|
|
@@ -72,7 +72,7 @@ _input => onSubmit(_input.value) // or input2: an outer input is shadowed
|
|
|
72
72
|
|
|
73
73
|
**Nearest sibling.** Extend the nearest implementation; match permissions, errors, refresh and tests. Reuse its helpers and change only affected state.
|
|
74
74
|
|
|
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
|
|
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.
|
|
76
76
|
|
|
77
77
|
```ts
|
|
78
78
|
// good
|
|
@@ -88,11 +88,11 @@ pipe(
|
|
|
88
88
|
)
|
|
89
89
|
```
|
|
90
90
|
|
|
91
|
-
**Canonical data.** Store
|
|
91
|
+
**Canonical data.** Store and send canonical data, derive display values, expose each state once and remove states no caller produces.
|
|
92
92
|
|
|
93
|
-
**One way.** Replace superseded paths; no duplicate
|
|
93
|
+
**One way.** Replace superseded paths; no duplicate, subset or superset fields, methods, options or exports.
|
|
94
94
|
|
|
95
|
-
**No compatibility.** Rename the shape and every caller together. No alias, fallback, deprecated path or flag unless a documented
|
|
95
|
+
**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
96
|
|
|
97
97
|
**Present needs.** No speculative option, abstraction, layer, export, file or check. Configure only what callers vary.
|
|
98
98
|
|
|
@@ -122,7 +122,7 @@ if (enabled === true) return // enabled is a boolean
|
|
|
122
122
|
if (props.selected) return // props.selected can be undefined
|
|
123
123
|
```
|
|
124
124
|
|
|
125
|
-
**Flat.** Use early-return statements in Effect.fn. No nested ternaries or combinators deeper than equivalent statements. Match
|
|
125
|
+
**Flat.** Use early-return statements in Effect.fn. No nested ternaries or combinators deeper than equivalent statements. Match and combinators serve single expressions; Boolean.match selects a value, except one-line JSX-attribute ternaries.
|
|
126
126
|
|
|
127
127
|
```ts
|
|
128
128
|
// good
|
|
@@ -185,7 +185,7 @@ const [first] = teamId
|
|
|
185
185
|
items.filter(item => supports(item)).map(item => item.value)
|
|
186
186
|
```
|
|
187
187
|
|
|
188
|
-
**Natives.** Use Effect modules instead of globals
|
|
188
|
+
**Natives.** Use Effect modules instead of globals and native methods. Third-party-owned methods stay, with an inline diagnostic disable and reason when needed.
|
|
189
189
|
|
|
190
190
|
```ts
|
|
191
191
|
// good
|
|
@@ -230,7 +230,7 @@ const text = Effect.fn('Ai.text')(function* (response: AiResponse) {
|
|
|
230
230
|
})
|
|
231
231
|
```
|
|
232
232
|
|
|
233
|
-
**Casts.** No `as`, `any` or erasing a known shape into `unknown`; `satisfies` and `as const` are the only assertions.
|
|
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.
|
|
234
234
|
|
|
235
235
|
```ts
|
|
236
236
|
// good
|
|
@@ -247,7 +247,7 @@ const params: unknown = part.params // erases an already-known shape
|
|
|
247
247
|
// good
|
|
248
248
|
const load = Effect.fn('Plugin.load')(function* (path: string) {
|
|
249
249
|
const endpoint = yield* Schema.decodeEffect(Endpoint)(config.endpoint)
|
|
250
|
-
const manifest = yield* Schema.decodeEffect(PackageManifest)(yield* fs.readFileString(path))
|
|
250
|
+
const manifest = yield* Schema.decodeEffect(Schema.fromJsonString(PackageManifest))(yield* fs.readFileString(path))
|
|
251
251
|
return {endpoint, manifest}
|
|
252
252
|
})
|
|
253
253
|
// bad
|
|
@@ -367,7 +367,7 @@ static readonly layer = Layer.effect(Ledger, makeLedger) // a make with one call
|
|
|
367
367
|
query(sql: string): Effect.Effect<Rows> // a method signature instead of a readonly property
|
|
368
368
|
```
|
|
369
369
|
|
|
370
|
-
**Primitives.** Use Effect's primitives directly; rpcs and services already trace, and one scope owns each lifetime. Compose established independent
|
|
370
|
+
**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.
|
|
371
371
|
|
|
372
372
|
```ts
|
|
373
373
|
// good
|
|
@@ -412,7 +412,7 @@ entries.sort((left, right) => left.tokens - right.tokens)
|
|
|
412
412
|
|
|
413
413
|
## Types and Schema
|
|
414
414
|
|
|
415
|
-
**Inference.** Infer types. Annotate recursion or branches TypeScript cannot unite; use schema types
|
|
415
|
+
**Inference.** Infer types. Annotate recursion or branches TypeScript cannot unite; use schema types and indexed members in signatures. Keep intentional widening, not repeated inferred generics.
|
|
416
416
|
|
|
417
417
|
```ts
|
|
418
418
|
// good
|
|
@@ -494,7 +494,7 @@ function isPathNotFound(error: unknown) {
|
|
|
494
494
|
|
|
495
495
|
## Shape
|
|
496
496
|
|
|
497
|
-
**Inline.** Inline forwarding wrappers and single-use helpers
|
|
497
|
+
**Inline.** Inline forwarding wrappers and single-use helpers, temporaries and constants. Argument-free functions are named values.
|
|
498
498
|
|
|
499
499
|
```ts
|
|
500
500
|
// good
|
|
@@ -573,7 +573,7 @@ onClick={() => Effect.runPromise(saveApp(draft))}
|
|
|
573
573
|
useAtomValue(Atom.make(loadApp(appId))) // a new atom on every render
|
|
574
574
|
```
|
|
575
575
|
|
|
576
|
-
**Outcomes.** Read data with `useAtomSuspense`. The route or section that owns a screen wraps it in one `Suspense` boundary for loading
|
|
576
|
+
**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.
|
|
577
577
|
|
|
578
578
|
```tsx
|
|
579
579
|
// good
|
|
@@ -593,7 +593,7 @@ const [pending, setPending] = useState(false) // copies the write atom's waiting
|
|
|
593
593
|
```
|
|
594
594
|
|
|
595
595
|
- **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.
|
|
596
|
-
- **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
|
|
596
|
+
- **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.
|
|
597
597
|
- **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.
|
|
598
598
|
|
|
599
599
|
Testing judgment, fixtures, assertions and verification belong to the testing skill; these engineering rules still apply to test code.
|
package/skills/testing/SKILL.md
CHANGED
|
@@ -1,16 +1,16 @@
|
|
|
1
1
|
---
|
|
2
2
|
name: testing
|
|
3
|
-
description: 'Select and run black-box behavior proof for Effect apps
|
|
3
|
+
description: 'Select and run black-box behavior proof for Effect apps and tools. Use when choosing, writing, reviewing or running tests; apply engineering to test code and keep exploratory proof question-sized.'
|
|
4
4
|
---
|
|
5
5
|
|
|
6
6
|
# Testing
|
|
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
|
|
10
|
+
- **Worth it.** Test owned branching, computation, parsing and state transitions that a plausible regression breaks. Add missing valuable coverage before refactoring.
|
|
11
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
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
|
|
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
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
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
16
|
- **Honest.** No wrong expectations or rule workarounds. Change assertions only for recorded behavior changes; preserve every still-relevant value.
|
|
@@ -38,26 +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 minimal implementation
|
|
42
|
-
- **Branches.** Exercise reachable empty
|
|
43
|
-
- **Failure.** Effect.flip for expected failure
|
|
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.
|
|
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
|
+
- **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
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.
|
|
45
45
|
|
|
46
46
|
## Effect and fixtures
|
|
47
47
|
|
|
48
|
-
- **Entrypoint.** Plain it
|
|
49
|
-
- **Lifetime.** it.effect
|
|
50
|
-
- **Isolation.** Shared layers share mutable services
|
|
51
|
-
- **Clock.** TestClock
|
|
52
|
-
- **Concurrency.**
|
|
53
|
-
- **Boundary.** Double only dependencies outside tested logic,
|
|
54
|
-
- **Artifacts.**
|
|
55
|
-
- **Platform.** FileSystem.layerNoop is not memory storage
|
|
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.
|
|
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.
|
|
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.
|
|
56
56
|
|
|
57
57
|
## Run and reconcile
|
|
58
58
|
|
|
59
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.
|
|
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
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
62
|
- **Stable inputs.** A check proves only the source, dependencies and configuration it ran on; an edit to them invalidates its result.
|
|
63
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.
|