@effect.healthcare/messenger 0.3.1 → 0.4.2
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/.claude/CLAUDE.md +88 -88
- package/.claude/context/composition.md +921 -921
- package/.claude/context/data.md +1036 -1036
- package/.claude/context/event.md +686 -686
- package/.claude/context/gesture.md +614 -614
- package/.claude/context/operation.md +596 -596
- package/.claude/context/state.md +864 -864
- package/.claude/context/testing.md +876 -876
- package/.claude/context/validation.md +350 -350
- package/.claude/context/workflow.md +608 -608
- package/.claude/settings.json +7 -0
- package/.mcp.json +12 -12
- package/LICENSE +144 -144
- package/dist/messenger.d.ts +9 -1
- package/dist/messenger.js +22 -3
- package/dist/messenger.meta.d.ts +1 -0
- package/dist/messenger.meta.js +1 -0
- package/package.json +55 -55
- package/src/index.ts +16 -16
- package/src/messenger.meta.ts +132 -131
- package/src/messenger.ts +386 -347
- package/src/messenger.validation.ts +25 -25
- package/web-dev-server.config.mjs +8 -8
- package/.claude/settings.local.json +0 -11
- package/dist/component.style.css +0 -6
- package/dist/component.template.html +0 -1
- package/dist/messenger.style.css +0 -6
- package/dist/messenger.template.html +0 -1
|
@@ -1,608 +1,608 @@
|
|
|
1
|
-
# Workflow
|
|
2
|
-
|
|
3
|
-
This document defines the **workflow policy** for component development. It specifies how implementation proceeds from feature branch creation through to merge — the sequence of commits within a feature, the ordering of concerns across the policy hierarchy, the structure of every commit message, and the conventions that govern the entire development flow.
|
|
4
|
-
|
|
5
|
-
This document is intentionally **workflow** only, but part of a set of implementation policies which together define the policies which should be followed to transform specifications into an implementation which is predictable, maintainable, and production-ready at scale.
|
|
6
|
-
|
|
7
|
-
Testing patterns, unit test structure, and TDD discipline are covered in a separate testing policy.
|
|
8
|
-
|
|
9
|
-
---
|
|
10
|
-
|
|
11
|
-
# How to Read This Policy
|
|
12
|
-
|
|
13
|
-
Rules in this policy fall into three strengths. Read each rule with its strength in mind:
|
|
14
|
-
|
|
15
|
-
- **Principles** are non-negotiable. They derive from framework correctness or TDD discipline and hold for every component. A conflict with a principle is a defect.
|
|
16
|
-
- **Templates** are invariant skeletons with explicit variation points. The skeleton applies to every component; specific steps activate or deactivate based on the component's declared classification (e.g., attribute-backed vs non-attribute, readonly vs writable, payload-bearing vs payload-free). Variation is part of the template, not a deviation from it.
|
|
17
|
-
- **Conventions** are canonical defaults that keep implementations readable across components. A second component may deviate when its shape genuinely warrants it; the deviation should be explained at review time, not forbidden by the policy.
|
|
18
|
-
|
|
19
|
-
When applying this policy to a new component, classify each rule before reconciling conflicts. A conflict with a principle is a defect; a conflict with a template means a variation point needs to be made explicit; a conflict with a convention is a documented deviation.
|
|
20
|
-
|
|
21
|
-
---
|
|
22
|
-
|
|
23
|
-
# 1) Every Feature Is Developed on an Isolated Branch
|
|
24
|
-
|
|
25
|
-
Each new feature is developed in isolation on a dedicated feature branch created from main.
|
|
26
|
-
|
|
27
|
-
A feature branch:
|
|
28
|
-
|
|
29
|
-
- is created from the main branch before any work begins
|
|
30
|
-
- contains all commits belonging to a single feature cycle
|
|
31
|
-
- is merged into main via a `FEATURE` commit
|
|
32
|
-
|
|
33
|
-
The `FEATURE` commit is always the last commit on the feature branch. It marks the boundary of the cycle and the merge point back into main.
|
|
34
|
-
|
|
35
|
-
No behavioral work is committed directly to main. Direct commits to main are limited to non-feature concerns such as `PACKAGE`, `TYPES`, post-merge naming corrections, version bumps, or refactors.
|
|
36
|
-
|
|
37
|
-
# 2) Every Commit Must Use a Canonical Prefix
|
|
38
|
-
|
|
39
|
-
Every commit message must begin with a canonical prefix from the following fixed vocabulary:
|
|
40
|
-
|
|
41
|
-
| Prefix | Domain |
|
|
42
|
-
| ---------------- | ------------------------------------------------------------------------------------------------------------- |
|
|
43
|
-
| `COMPOSITION` | DOM structure and cached element references |
|
|
44
|
-
| `STATE` | State contract — metadata, type, enum values, getter, setter, mutation, attribute sync |
|
|
45
|
-
| `VALIDATION` | Input validation — `Validate` class methods and error boundaries (including setter-level invalid-value tests) |
|
|
46
|
-
| `OPERATION` | Public methods — metadata, existence, and behavioral outcome |
|
|
47
|
-
| `EVENT` | Event contract — metadata, setter, handler behavior, and payload |
|
|
48
|
-
| `GESTURE` | Interaction wiring — metadata and behavioral outcome |
|
|
49
|
-
| `DATA` | Component-owned data contract |
|
|
50
|
-
| `STYLE` | CSS rules |
|
|
51
|
-
| `SPECIFICATIONS` | Component specification document |
|
|
52
|
-
| `TESTING` | Test vocabulary corrections |
|
|
53
|
-
| `DOCUMENTATION` | Generated API documentation |
|
|
54
|
-
| `FEATURE` | Feature boundary marker |
|
|
55
|
-
| `PACKAGE` | Package metadata |
|
|
56
|
-
| `CONFIG` | Test and build infrastructure |
|
|
57
|
-
| `TYPES` | TypeScript type definitions independent of runtime behavior |
|
|
58
|
-
|
|
59
|
-
The prefix identifies the concern domain of the commit. It must always be uppercase and must be the first token in the commit message, followed by `: `.
|
|
60
|
-
|
|
61
|
-
Every commit must use exactly one prefix. The `Initial commit` and version-tag commits (e.g. `0.1.0`) are the only permitted exceptions.
|
|
62
|
-
|
|
63
|
-
`CONFIG`, `TYPES`, `TESTING`, and refactor prefixes are reserved for their respective concerns and may not appear in every feature cycle.
|
|
64
|
-
|
|
65
|
-
# 3) Every Behavioral Change Requires a FAIL/PASS Commit Pair
|
|
66
|
-
|
|
67
|
-
Every behavioral change — `COMPOSITION`, `STATE`, `VALIDATION`, `OPERATION`, `EVENT`, `GESTURE`, or `DATA` — must be introduced as a FAIL/PASS commit pair.
|
|
68
|
-
|
|
69
|
-
Structure:
|
|
70
|
-
|
|
71
|
-
- The `FAIL` commit introduces the test that describes the expected behavior. The test must fail at the point of commit.
|
|
72
|
-
- The `PASS` commit introduces the minimum implementation required to make the test pass. The test must pass at the point of commit.
|
|
73
|
-
|
|
74
|
-
Rules:
|
|
75
|
-
|
|
76
|
-
- The `FAIL` commit must precede the `PASS` commit.
|
|
77
|
-
- The `FAIL` and `PASS` commits must always be adjacent — no other commits may appear between them.
|
|
78
|
-
- Every `FAIL` must have a corresponding `PASS`.
|
|
79
|
-
- No behavioral implementation may be committed without a preceding `FAIL` commit.
|
|
80
|
-
|
|
81
|
-
`STYLE`, `SPECIFICATIONS`, `DOCUMENTATION`, `FEATURE`, `PACKAGE`, `CONFIG`, `TYPES`, `TESTING`, and refactor commits do not require a FAIL/PASS pair.
|
|
82
|
-
|
|
83
|
-
# 4) The Feature Development Sequence Is Policy-Ordered
|
|
84
|
-
|
|
85
|
-
Within a behavioral feature, commits must follow this canonical order:
|
|
86
|
-
|
|
87
|
-
```
|
|
88
|
-
COMPOSITION
|
|
89
|
-
STATE
|
|
90
|
-
VALIDATION
|
|
91
|
-
OPERATION
|
|
92
|
-
EVENT
|
|
93
|
-
STYLE (state-driven)
|
|
94
|
-
GESTURE
|
|
95
|
-
DOCUMENTATION
|
|
96
|
-
FEATURE
|
|
97
|
-
```
|
|
98
|
-
|
|
99
|
-
This ordering reflects the dependency structure of the implementation. Each layer depends on the layers above it, so work proceeds top-down through the policy hierarchy.
|
|
100
|
-
|
|
101
|
-
`STYLE` for state-driven rules appears after `EVENT` because the visual states it reflects are fully established only once the state and event contracts are in place.
|
|
102
|
-
|
|
103
|
-
`GESTURE` follows state-driven `STYLE` because gesture wiring depends on cached element references, which in turn depend on the composition being final.
|
|
104
|
-
|
|
105
|
-
Deviations from this order are permitted only under the just-in-time rule (see Section 14).
|
|
106
|
-
|
|
107
|
-
# 5) COMPOSITION Commits Follow an Outside-In Structural Order
|
|
108
|
-
|
|
109
|
-
`COMPOSITION` commits introduce structural DOM parts and cached element references.
|
|
110
|
-
|
|
111
|
-
Within a feature, `COMPOSITION` commits must precede all other behavioral commits, except for the cached-reference commit which is placed just-in-time between `GESTURE` metadata and `GESTURE` behavior (see Section 10).
|
|
112
|
-
|
|
113
|
-
Ordering rules:
|
|
114
|
-
|
|
115
|
-
- Container elements are introduced before their children.
|
|
116
|
-
- Parent-child structural relationships use the word `contains` in the commit description.
|
|
117
|
-
- Cached element references are a `COMPOSITION` concern and must use the `COMPOSITION` prefix.
|
|
118
|
-
|
|
119
|
-
Example:
|
|
120
|
-
|
|
121
|
-
```
|
|
122
|
-
COMPOSITION: `pin.root` contains `div.icon` -> FAIL
|
|
123
|
-
COMPOSITION: `pin.root` contains `div.icon` -> PASS
|
|
124
|
-
COMPOSITION: `div.icon` contains `svg.unpinned` -> FAIL
|
|
125
|
-
COMPOSITION: `div.icon` contains `svg.unpinned` -> PASS
|
|
126
|
-
```
|
|
127
|
-
|
|
128
|
-
Structural parts are added incrementally across features. Only the parts required by the current feature are introduced in that feature's cycle.
|
|
129
|
-
|
|
130
|
-
# 6) STATE Commits Follow a Fixed Coverage Sequence
|
|
131
|
-
|
|
132
|
-
For each state property introduced in a feature, `STATE` commits must follow this order:
|
|
133
|
-
|
|
134
|
-
1. **Metadata pair** — `Attributes.X` and `State.X` tested together in one commit
|
|
135
|
-
2. **Type** — the value-domain type for the state property (e.g. `Visibility`, `Status`)
|
|
136
|
-
3. **Enum values** — tested in source-declaration order (the order they appear in `<component>.meta.ts`)
|
|
137
|
-
4. **Getter existence**
|
|
138
|
-
5. **Default value** — the internal runtime default returned by the getter
|
|
139
|
-
6. **Attribute default** — only when the attribute is compulsory and always written to the DOM (two commits: `<attribute> attribute exists` and `<attribute> attribute is <default>`)
|
|
140
|
-
7. **Setter existence**
|
|
141
|
-
8. **Mutation persists** — setting the value via the setter updates the internal state
|
|
142
|
-
9. **Null normalization** — only when the setter normalizes `null` to a default value
|
|
143
|
-
10. **Attribute synchronization (state → attribute)** — setter assignment updates the DOM attribute
|
|
144
|
-
11. **Attribute synchronization (attribute → state)** — `setAttribute` drives the getter
|
|
145
|
-
|
|
146
|
-
Steps 6 and 9 are conditional:
|
|
147
|
-
|
|
148
|
-
- Include attribute-default only when the attribute is compulsory and always present in the DOM.
|
|
149
|
-
- Include null normalization only when the setter contains normalization logic (`value ?? default`).
|
|
150
|
-
|
|
151
|
-
The validation-boundary commit — `set <component>.<state> to invalid throws error` — is logically a STATE-layer assertion but is committed under the `VALIDATION:` prefix. It appears immediately after the `VALIDATION` method-existence and method-error commits (see Section 7).
|
|
152
|
-
|
|
153
|
-
# 7) VALIDATION Commits Are Placed Between STATE and OPERATION
|
|
154
|
-
|
|
155
|
-
`VALIDATION` commits introduce the `Validate` class methods that enforce input boundaries.
|
|
156
|
-
|
|
157
|
-
Within a feature, `VALIDATION` commits appear after the main body of `STATE` commits and before `OPERATION` commits.
|
|
158
|
-
|
|
159
|
-
The validation block has three commit pairs:
|
|
160
|
-
|
|
161
|
-
1. `VALIDATION: \`Validate.x\` static method exists`— verifies the method on the`Validate` class
|
|
162
|
-
2. `VALIDATION: \`Validate.x(value)\` throws error if \`value\` is \`invalid\`` — verifies the validator-level error
|
|
163
|
-
3. `VALIDATION: set \`pin.x\` to \`invalid\` throws error` — verifies that the setter propagates the validator error
|
|
164
|
-
|
|
165
|
-
The third commit is the setter-level validation boundary. Although it asserts on the STATE setter, it remains conceptually part of the validation contract and uses the `VALIDATION:` prefix.
|
|
166
|
-
|
|
167
|
-
Sequence:
|
|
168
|
-
|
|
169
|
-
```
|
|
170
|
-
STATE: ... (main body, steps 1-11 excluding the boundary)
|
|
171
|
-
VALIDATION: `Validate.x` static method exists -> FAIL/PASS
|
|
172
|
-
VALIDATION: `Validate.x(value)` throws error if `value` is `invalid` -> FAIL/PASS
|
|
173
|
-
VALIDATION: set `pin.x` to `invalid` throws error -> FAIL/PASS
|
|
174
|
-
OPERATION: ...
|
|
175
|
-
```
|
|
176
|
-
|
|
177
|
-
# 8) OPERATION Commits Follow a Semantic-Group Batch-Then-Sequence Pattern
|
|
178
|
-
|
|
179
|
-
Operations are grouped semantically — paired operations such as `hide`/`show` or `pin`/`unpin` belong to one group; composite or derived operations such as `toggle` belong to a separate group.
|
|
180
|
-
|
|
181
|
-
For each semantic group:
|
|
182
|
-
|
|
183
|
-
1. Test all metadata constants for the group first — all `Operation.X exists` commits for the group appear before any method or behavior commit in that group.
|
|
184
|
-
2. For each operation in the group: method exists → `Invoke` behavior.
|
|
185
|
-
|
|
186
|
-
Behavior commit rules:
|
|
187
|
-
|
|
188
|
-
- Use `Invoke` as the uniform verb for all operation behavior commits.
|
|
189
|
-
- Include a precondition in the commit description when the operation is only semantically meaningful from a specific prior state (e.g., `show` requires the component to already be hidden).
|
|
190
|
-
- Omit the precondition when the operation can be meaningfully called from the default state.
|
|
191
|
-
- When an operation covers a bidirectional transition, state both directions in one commit description using `from X to Y and visa versa`.
|
|
192
|
-
|
|
193
|
-
Example — paired group followed by composite group:
|
|
194
|
-
|
|
195
|
-
```
|
|
196
|
-
OPERATION: `Operation.PIN` exists -> FAIL/PASS
|
|
197
|
-
OPERATION: `Operation.UNPIN` exists -> FAIL/PASS
|
|
198
|
-
OPERATION: `pin.pin` method exists -> FAIL/PASS
|
|
199
|
-
OPERATION: Invoke `pin.pin` sets `pin.status` to `Status.PINNED` -> FAIL/PASS
|
|
200
|
-
OPERATION: `pin.unpin` method exists -> FAIL/PASS
|
|
201
|
-
OPERATION: Invoke `pin.unpin` when `pin.status` is `Status.PINNED` sets `pin.status` to `Status.UNPINNED` -> FAIL/PASS
|
|
202
|
-
OPERATION: `Operation.TOGGLE` exists -> FAIL/PASS
|
|
203
|
-
OPERATION: `pin.toggle` method exists -> FAIL/PASS
|
|
204
|
-
OPERATION: Invoke `pin.toggle` toggles `pin.status` from `Status.UNPINNED` to `Status.PINNED` and visa versa -> FAIL/PASS
|
|
205
|
-
```
|
|
206
|
-
|
|
207
|
-
# 9) EVENT Commits Follow a Three-Per-Event Pattern
|
|
208
|
-
|
|
209
|
-
For each semantic event group, `EVENT` commits follow this pattern:
|
|
210
|
-
|
|
211
|
-
1. Test all event metadata constants for the group first — all `Event.ON_X exists` commits for the group appear before any setter, handler, or payload commit.
|
|
212
|
-
2. For each event in the group, three behavioral commit pairs:
|
|
213
|
-
- **Setter existence** — `pin.on{x} setter exists`
|
|
214
|
-
- **Handler trigger** — `pin.on{x} listener is called when pin.<state> changes from <Before> to <After>`
|
|
215
|
-
- **Payload contract** — `When pin.on{x} listener is called then it is called with \`{ detail: { ... } }\``
|
|
216
|
-
|
|
217
|
-
Handler-trigger commit rules:
|
|
218
|
-
|
|
219
|
-
- Describe the full state transition using `from X to Y` phrasing.
|
|
220
|
-
- The direction must be explicit in the commit description — the before and after state values must both be named.
|
|
221
|
-
|
|
222
|
-
Example:
|
|
223
|
-
|
|
224
|
-
```
|
|
225
|
-
EVENT: `Event.ON_HIDE` exists -> FAIL/PASS
|
|
226
|
-
EVENT: `Event.ON_SHOW` exists -> FAIL/PASS
|
|
227
|
-
EVENT: `pin.onhide` setter exists -> FAIL/PASS
|
|
228
|
-
EVENT: `pin.onhide` listener is called when `pin.visibility` changes from `Visibility.VISIBLE` to `Visibility.HIDDEN` -> FAIL/PASS
|
|
229
|
-
EVENT: When `pin.onhide` listener is called then it is called with `{ detail: { visibility: Visibility.HIDDEN } }` -> FAIL/PASS
|
|
230
|
-
EVENT: `pin.onshow` setter exists -> FAIL/PASS
|
|
231
|
-
EVENT: `pin.onshow` listener is called when `pin.visibility` changes from `Visibility.HIDDEN` to `Visibility.VISIBLE` -> FAIL/PASS
|
|
232
|
-
EVENT: When `pin.onshow` listener is called then it is called with `{ detail: { visibility: Visibility.VISIBLE } }` -> FAIL/PASS
|
|
233
|
-
```
|
|
234
|
-
|
|
235
|
-
# 10) GESTURE Commits Bracket the COMPOSITION Cached Reference
|
|
236
|
-
|
|
237
|
-
`GESTURE` commits introduce interaction wiring.
|
|
238
|
-
|
|
239
|
-
Within a feature, `GESTURE` commits appear after `STYLE` and before `DOCUMENTATION`.
|
|
240
|
-
|
|
241
|
-
The `COMPOSITION` cached element reference commit is placed just-in-time between the gesture metadata commit and the gesture behavior commit — not at the start of the feature cycle — because the cache is the direct prerequisite for listener wiring and not needed before that point.
|
|
242
|
-
|
|
243
|
-
Order:
|
|
244
|
-
|
|
245
|
-
```
|
|
246
|
-
GESTURE: `Gesture.X` exists -> FAIL/PASS
|
|
247
|
-
COMPOSITION: `pin.elements.x` is a cached reference to the `<element>` with class `<class>` -> FAIL/PASS
|
|
248
|
-
GESTURE: `Gesture.X` on `<element>` with class `<class>` <behavior> -> FAIL/PASS
|
|
249
|
-
```
|
|
250
|
-
|
|
251
|
-
GESTURE behavior descriptions identify the realized DOM element using selector phrasing (`\`div\` with class \`icon\``), not by the cached reference name. This keeps the commit description aligned with how the test exercises the interaction — by querying the realized DOM.
|
|
252
|
-
|
|
253
|
-
When a gesture behavior covers a bidirectional toggle, both directions are stated in a single behavior commit description using `from X to Y and visa versa`.
|
|
254
|
-
|
|
255
|
-
Example:
|
|
256
|
-
|
|
257
|
-
```
|
|
258
|
-
GESTURE: `Gesture.CLICK` exists -> FAIL/PASS
|
|
259
|
-
COMPOSITION: `pin.elements.icon` is a cached reference to the `div` with class `icon` -> FAIL/PASS
|
|
260
|
-
GESTURE: `Gesture.CLICK` on `div` with class `icon` toggle `pin.status` from `Status.UNPINNED` to `Status.PINNED` and visa versa -> FAIL/PASS
|
|
261
|
-
```
|
|
262
|
-
|
|
263
|
-
# 11) STYLE Commits Are Placed at the Point of Visual Readiness
|
|
264
|
-
|
|
265
|
-
`STYLE` commits are placed at the earliest point in the feature cycle where the elements and state they depend on are fully established.
|
|
266
|
-
|
|
267
|
-
Two placement rules apply:
|
|
268
|
-
|
|
269
|
-
- **Base element styles** — styles that apply to DOM elements with no state dependency may be committed immediately after the `COMPOSITION` commits that establish those elements. These can appear before `STATE` commits if the elements already exist.
|
|
270
|
-
- **State-driven styles** — styles whose selectors depend on state attributes (`:host([attribute="value"])`) must appear after all `EVENT` commits for that state, since the full state contract must be in place before its visual representation is added.
|
|
271
|
-
|
|
272
|
-
All `STYLE` commits must appear before `DOCUMENTATION`.
|
|
273
|
-
|
|
274
|
-
`STYLE` commit rules:
|
|
275
|
-
|
|
276
|
-
- `STYLE` commits never have a FAIL/PASS pair.
|
|
277
|
-
- Use `Define` for base element styles — rules with no state dependency that establish a default appearance for a structural part.
|
|
278
|
-
- Use `Add` for state-driven styles, media-query styles, hover/focus styles, and any style that extends an existing visual contract.
|
|
279
|
-
- One selector (or one logically-grouped selector list bound to a single rule) per commit. Complementary visual states (e.g., pinned rules and unpinned rules) are separated into distinct `STYLE` commits.
|
|
280
|
-
- `@media` blocks are committed per rule inside the block — the media query is part of the commit description, not a wrapper around multiple selector commits.
|
|
281
|
-
|
|
282
|
-
Examples:
|
|
283
|
-
|
|
284
|
-
```
|
|
285
|
-
STYLE: Define `svg` style
|
|
286
|
-
STYLE: Define `.icon` style
|
|
287
|
-
STYLE: Add `:host([visibility="hidden"])` style
|
|
288
|
-
STYLE: Add `:host([status="pinned"]) .icon` style
|
|
289
|
-
STYLE: Add `@media (hover: hover) .icon:hover svg` style
|
|
290
|
-
STYLE: Add `@media (prefers-color-scheme: dark) :host` style
|
|
291
|
-
```
|
|
292
|
-
|
|
293
|
-
# 12) CSS-Only Features Follow a Collapsed Sequence
|
|
294
|
-
|
|
295
|
-
When a feature introduces only visual changes with no new behavioral contract, it follows a collapsed cycle with no `FAIL/PASS` pairs and no `DOCUMENTATION` commit:
|
|
296
|
-
|
|
297
|
-
```
|
|
298
|
-
STYLE
|
|
299
|
-
FEATURE
|
|
300
|
-
```
|
|
301
|
-
|
|
302
|
-
This applies to features whose entire change is within CSS — such as hover responsiveness or dark mode support. No behavioral commit sequence is required.
|
|
303
|
-
|
|
304
|
-
# 13) DOCUMENTATION Closes Every Behavioral Feature
|
|
305
|
-
|
|
306
|
-
After all `FAIL/PASS` pairs and `STYLE` commits are complete, the behavioral feature cycle closes with:
|
|
307
|
-
|
|
308
|
-
```
|
|
309
|
-
DOCUMENTATION
|
|
310
|
-
FEATURE
|
|
311
|
-
```
|
|
312
|
-
|
|
313
|
-
Rules:
|
|
314
|
-
|
|
315
|
-
- `DOCUMENTATION` is always generated after implementation is complete. It must not precede any behavioral commits.
|
|
316
|
-
- `FEATURE` follows `DOCUMENTATION`. It is the boundary commit for the branch.
|
|
317
|
-
|
|
318
|
-
# 14) Just-in-Time Addition of Infrastructure and Composition
|
|
319
|
-
|
|
320
|
-
Not all `COMPOSITION` parts and `CONFIG` infrastructure are added at the start of a feature cycle. They are added at the earliest point in the sequence where they become necessary.
|
|
321
|
-
|
|
322
|
-
Rules:
|
|
323
|
-
|
|
324
|
-
- A `CONFIG` commit introducing test infrastructure appears immediately before the first commit that requires it.
|
|
325
|
-
- A `COMPOSITION` commit for a structural part appears at the start of the feature cycle that first requires that part.
|
|
326
|
-
- A `COMPOSITION` cached reference commit appears between `GESTURE` metadata and `GESTURE` behavior — not at the start of the cycle — because the cache is the direct prerequisite for listener wiring.
|
|
327
|
-
|
|
328
|
-
The just-in-time principle avoids speculative additions and keeps every commit traceable to the specific need that drove it.
|
|
329
|
-
|
|
330
|
-
# 15) Commit Messages Must Follow a Canonical Format
|
|
331
|
-
|
|
332
|
-
Every commit message must conform to one of the following formats:
|
|
333
|
-
|
|
334
|
-
**Behavioral commit (FAIL):**
|
|
335
|
-
|
|
336
|
-
```
|
|
337
|
-
{PREFIX}: {description} -> FAIL
|
|
338
|
-
```
|
|
339
|
-
|
|
340
|
-
**Behavioral commit (PASS):**
|
|
341
|
-
|
|
342
|
-
```
|
|
343
|
-
{PREFIX}: {description} -> PASS
|
|
344
|
-
```
|
|
345
|
-
|
|
346
|
-
**Non-behavioral commit:**
|
|
347
|
-
|
|
348
|
-
```
|
|
349
|
-
{PREFIX}: {description}
|
|
350
|
-
```
|
|
351
|
-
|
|
352
|
-
Formatting rules within descriptions:
|
|
353
|
-
|
|
354
|
-
- Code references — types, values, methods, attributes — must be wrapped in backticks.
|
|
355
|
-
- Structural relationships use `contains` as the verb.
|
|
356
|
-
- Operation behavior commits begin with `Invoke`.
|
|
357
|
-
- Event handler behavior commits include `from X to Y` for state transitions.
|
|
358
|
-
- Event payload commits begin with `When ... listener is called then it is called with` followed by the backticked `{ detail: { ... } }` payload.
|
|
359
|
-
- Gesture behavior commits identify the target element by selector phrasing (`\`div\` with class \`icon\``), not by cached reference name.
|
|
360
|
-
- Bidirectional toggle behaviors state both directions: `from X to Y and visa versa`.
|
|
361
|
-
- Preconditions use `when {subject} is {value}` phrasing.
|
|
362
|
-
|
|
363
|
-
# 16) Commit-Message Hygiene Is a Reviewable Concern
|
|
364
|
-
|
|
365
|
-
Commit messages are part of the historical record and must be reviewed with the same discipline as code.
|
|
366
|
-
|
|
367
|
-
Policy:
|
|
368
|
-
|
|
369
|
-
- The `{PREFIX}: ` token must not be omitted from a behavioral commit.
|
|
370
|
-
- State values, event names, and operation names referenced in the description must match the values actually exercised by the test and the implementation.
|
|
371
|
-
- Typos in the feature name (the `FEATURE: ` boundary commit) must be corrected before merge.
|
|
372
|
-
- A commit message that misnames a state value, event handler, or operation is a defect even if the test and implementation are correct.
|
|
373
|
-
|
|
374
|
-
A standalone `TESTING: Update naming convention` commit may be used to correct historical commit-message drift only when the drift is in the test vocabulary itself. Correcting an isolated commit-message typo retroactively is not in scope — the discipline is to catch it before merge.
|
|
375
|
-
|
|
376
|
-
# 17) Naming Convention Corrections Are Standalone Commits
|
|
377
|
-
|
|
378
|
-
When test vocabulary drifts and requires correction, the correction is committed as a standalone `TESTING: Update naming convention` commit.
|
|
379
|
-
|
|
380
|
-
This commit must not be bundled with behavioral changes. It is standalone.
|
|
381
|
-
|
|
382
|
-
# 18) Refactor Commits Use the Domain Prefix
|
|
383
|
-
|
|
384
|
-
When implementation is refactored without changing behavior:
|
|
385
|
-
|
|
386
|
-
- Use the domain prefix of the area being refactored (`STATE: refactor ...`, not a separate `REFACTOR` prefix)
|
|
387
|
-
- Include `refactor` as a verb in the commit message body
|
|
388
|
-
- Do not use a FAIL/PASS pair — behavior does not change under a refactor
|
|
389
|
-
|
|
390
|
-
# 19) Non-Behavioral Prefixes Have Dedicated Scopes
|
|
391
|
-
|
|
392
|
-
Non-behavioral concerns use their own prefixes and are not embedded in behavioral commit sequences:
|
|
393
|
-
|
|
394
|
-
- `SPECIFICATIONS` — Component specification document. Appears at project initialization when the canonical specification is added or when the specification is updated independently of implementation.
|
|
395
|
-
- `TYPES` — TypeScript type changes independent of runtime behavior. May appear post-feature when a type correction is identified after merge.
|
|
396
|
-
- `PACKAGE` — Package metadata. Appears at project initialization or when package configuration changes.
|
|
397
|
-
- `CONFIG` — Test and build infrastructure. Added just-in-time when a new capability is needed.
|
|
398
|
-
|
|
399
|
-
---
|
|
400
|
-
|
|
401
|
-
# Policy Applied to Specification
|
|
402
|
-
|
|
403
|
-
The Pin component was developed across four feature cycles. The following shows how the workflow policy maps onto each cycle.
|
|
404
|
-
|
|
405
|
-
## Project Initialization
|
|
406
|
-
|
|
407
|
-
Direct commits to main:
|
|
408
|
-
|
|
409
|
-
```
|
|
410
|
-
Initial commit
|
|
411
|
-
SPECIFICATIONS: Add `pin.specifications.json`
|
|
412
|
-
PACKAGE: Update package metadata
|
|
413
|
-
DOCUMENTATION: Generate API Docs
|
|
414
|
-
```
|
|
415
|
-
|
|
416
|
-
## Feature: Visibility Control
|
|
417
|
-
|
|
418
|
-
Branch: `feature/visibility-control`
|
|
419
|
-
|
|
420
|
-
Behavioral feature — full cycle:
|
|
421
|
-
|
|
422
|
-
```
|
|
423
|
-
COMPOSITION: `pin.root` contains `div.icon` -> FAIL/PASS
|
|
424
|
-
COMPOSITION: `div.icon` contains `svg.unpinned` -> FAIL/PASS
|
|
425
|
-
STYLE: Define `.icon` style
|
|
426
|
-
STYLE: Define `svg` style
|
|
427
|
-
STATE: `Attributes.VISIBILITY` and `State.VISIBILITY` exists -> FAIL/PASS
|
|
428
|
-
STATE: `Visibility` exists -> FAIL/PASS
|
|
429
|
-
STATE: `Visibility.VISIBLE` exists -> FAIL/PASS
|
|
430
|
-
STATE: `Visibility.HIDDEN` exists -> FAIL/PASS
|
|
431
|
-
STATE: `pin.visibility` getter exists -> FAIL/PASS
|
|
432
|
-
STATE: `pin.visibility` getter returns `Visibility.VISIBLE` -> FAIL/PASS
|
|
433
|
-
STATE: `pin.visibility` setter exists -> FAIL/PASS
|
|
434
|
-
STATE: set `pin.visibility` to `Visibility.HIDDEN` updates state -> FAIL/PASS
|
|
435
|
-
STATE: set `pin.visibility` to `null` updates state to `Visibility.VISIBLE` -> FAIL/PASS
|
|
436
|
-
STATE: set `pin.visibility` to `Visibility.HIDDEN` updates attribute -> FAIL/PASS
|
|
437
|
-
STATE: set `visibility` attribute to `Visibility.HIDDEN` updates state -> FAIL/PASS
|
|
438
|
-
VALIDATION: `Validate.visibility` static method exists -> FAIL/PASS
|
|
439
|
-
VALIDATION: `Validate.visibility(value)` throws error if `value` is `invalid` -> FAIL/PASS
|
|
440
|
-
VALIDATION: set `pin.visibility` to `invalid` throws error -> FAIL/PASS
|
|
441
|
-
OPERATION: `Operation.HIDE` exists -> FAIL/PASS
|
|
442
|
-
OPERATION: `Operation.SHOW` exists -> FAIL/PASS
|
|
443
|
-
OPERATION: `pin.hide` method exists -> FAIL/PASS
|
|
444
|
-
OPERATION: Invoke `pin.hide` sets `pin.visibility` to `Visibility.HIDDEN` -> FAIL/PASS
|
|
445
|
-
OPERATION: `pin.show` method exists -> FAIL/PASS
|
|
446
|
-
OPERATION: Invoke `pin.show` when `pin.visibility` is `Visibility.HIDDEN` sets `pin.visibility` to `Visibility.VISIBLE` -> FAIL/PASS
|
|
447
|
-
EVENT: `Event.ON_HIDE` exists -> FAIL/PASS
|
|
448
|
-
EVENT: `Event.ON_SHOW` exists -> FAIL/PASS
|
|
449
|
-
EVENT: `pin.onhide` setter exists -> FAIL/PASS
|
|
450
|
-
EVENT: `pin.onhide` listener is called when `pin.visibility` changes from `Visibility.VISIBLE` to `Visibility.HIDDEN` -> FAIL/PASS
|
|
451
|
-
EVENT: When `pin.onhide` listener is called then it is called with `{ detail: { visibility: Visibility.HIDDEN } }` -> FAIL/PASS
|
|
452
|
-
EVENT: `pin.onshow` setter exists -> FAIL/PASS
|
|
453
|
-
EVENT: `pin.onshow` listener is called when `pin.visibility` changes from `Visibility.HIDDEN` to `Visibility.VISIBLE` -> FAIL/PASS
|
|
454
|
-
EVENT: When `pin.onshow` listener is called then it is called with `{ detail: { visibility: Visibility.VISIBLE } }` -> FAIL/PASS
|
|
455
|
-
STYLE: Add `:host([visibility="hidden"])` style
|
|
456
|
-
DOCUMENTATION: Generate API Docs
|
|
457
|
-
FEATURE: Visibility Control
|
|
458
|
-
```
|
|
459
|
-
|
|
460
|
-
How the policy is applied:
|
|
461
|
-
|
|
462
|
-
- `COMPOSITION` parts added outside-in at the start of the cycle
|
|
463
|
-
- Base `STYLE` (`.icon`, `svg`) added just-in-time after the composed elements exist, before `STATE`
|
|
464
|
-
- `STATE` coverage follows the fixed sequence: metadata → type → enum values (source order) → getter → default → setter → mutation → null normalization → state→attribute → attribute→state
|
|
465
|
-
- Enum values tested in source-declaration order: `Visibility.VISIBLE` before `Visibility.HIDDEN`
|
|
466
|
-
- Null normalization included because `visibility` normalizes `null` to `Visibility.VISIBLE`
|
|
467
|
-
- Attribute-default omitted because `visibility` is optional — the attribute is absent when the value is the default
|
|
468
|
-
- `VALIDATION` commits include the method-existence pair, the method-error pair, and the setter-level boundary commit — all under the `VALIDATION:` prefix
|
|
469
|
-
- `OPERATION` metadata (`HIDE`, `SHOW`) batched before any method commits
|
|
470
|
-
- `EVENT` metadata (`ON_HIDE`, `ON_SHOW`) batched before any setter, handler, or payload commits
|
|
471
|
-
- Each event has three behavioral commits: setter existence, handler trigger, payload contract
|
|
472
|
-
- State-driven `STYLE` follows `EVENT`
|
|
473
|
-
- No `GESTURE` in this feature — visibility is controlled imperatively only
|
|
474
|
-
- `DOCUMENTATION` → `FEATURE` close the cycle
|
|
475
|
-
|
|
476
|
-
## Feature: Pin and Unpin
|
|
477
|
-
|
|
478
|
-
Branch: `feature/pin-unpin`
|
|
479
|
-
|
|
480
|
-
Behavioral feature — full cycle:
|
|
481
|
-
|
|
482
|
-
```
|
|
483
|
-
COMPOSITION: `div.icon` contains `svg.pinned` -> FAIL/PASS
|
|
484
|
-
STATE: `Attributes.STATUS` and `State.STATUS` exists -> FAIL/PASS
|
|
485
|
-
STATE: `Status` exists -> FAIL/PASS
|
|
486
|
-
STATE: `Status.PINNED` exists -> FAIL/PASS
|
|
487
|
-
STATE: `Status.UNPINNED` exists -> FAIL/PASS
|
|
488
|
-
STATE: `pin.status` getter exists -> FAIL/PASS
|
|
489
|
-
STATE: `pin.status` getter returns `Status.UNPINNED` -> FAIL/PASS
|
|
490
|
-
STATE: `status` attribute exists -> FAIL/PASS
|
|
491
|
-
STATE: `status` attribute is `Status.UNPINNED` -> FAIL/PASS
|
|
492
|
-
STATE: `pin.status` setter exists -> FAIL/PASS
|
|
493
|
-
STATE: set `pin.status` to `Status.PINNED` updates state -> FAIL/PASS
|
|
494
|
-
STATE: set `pin.status` to `Status.PINNED` updates attribute -> FAIL/PASS
|
|
495
|
-
STATE: set `status` attribute to `Status.PINNED` updates state -> FAIL/PASS
|
|
496
|
-
VALIDATION: `Validate.status` static method exists -> FAIL/PASS
|
|
497
|
-
VALIDATION: `Validate.status(value)` throws error if `value` is `invalid` -> FAIL/PASS
|
|
498
|
-
VALIDATION: set `pin.status` to `invalid` throws error -> FAIL/PASS
|
|
499
|
-
OPERATION: `Operation.PIN` exists -> FAIL/PASS
|
|
500
|
-
OPERATION: `Operation.UNPIN` exists -> FAIL/PASS
|
|
501
|
-
OPERATION: `pin.pin` method exists -> FAIL/PASS
|
|
502
|
-
OPERATION: Invoke `pin.pin` sets `pin.status` to `Status.PINNED` -> FAIL/PASS
|
|
503
|
-
OPERATION: `pin.unpin` method exists -> FAIL/PASS
|
|
504
|
-
OPERATION: Invoke `pin.unpin` when `pin.status` is `Status.PINNED` sets `pin.status` to `Status.UNPINNED` -> FAIL/PASS
|
|
505
|
-
OPERATION: `Operation.TOGGLE` exists -> FAIL/PASS
|
|
506
|
-
OPERATION: `pin.toggle` method exists -> FAIL/PASS
|
|
507
|
-
OPERATION: Invoke `pin.toggle` toggles `pin.status` from `Status.UNPINNED` to `Status.PINNED` and visa versa -> FAIL/PASS
|
|
508
|
-
EVENT: `Event.ON_PIN` exists -> FAIL/PASS
|
|
509
|
-
EVENT: `Event.ON_UNPIN` exists -> FAIL/PASS
|
|
510
|
-
EVENT: `pin.onpin` setter exists -> FAIL/PASS
|
|
511
|
-
EVENT: `pin.onpin` listener is called when `pin.status` changes from `Status.UNPINNED` to `Status.PINNED` -> FAIL/PASS
|
|
512
|
-
EVENT: When `pin.onpin` listener is called then it is called with `{ detail: { status: Status.PINNED } }` -> FAIL/PASS
|
|
513
|
-
EVENT: `pin.onunpin` setter exists -> FAIL/PASS
|
|
514
|
-
EVENT: `pin.onunpin` listener is called when `pin.status` changes from `Status.PINNED` to `Status.UNPINNED` -> FAIL/PASS
|
|
515
|
-
EVENT: When `pin.onunpin` listener is called then it is called with `{ detail: { status: Status.UNPINNED } }` -> FAIL/PASS
|
|
516
|
-
STYLE: Add `.unpinned` style
|
|
517
|
-
STYLE: Add `:host([status="unpinned"]) .pinned` style
|
|
518
|
-
STYLE: Add `:host([status="unpinned"]) .unpinned` style
|
|
519
|
-
STYLE: Add `.pinned` style
|
|
520
|
-
STYLE: Add `:host([status="pinned"]) .icon` style
|
|
521
|
-
STYLE: Add `:host([status="pinned"]) .pinned` style
|
|
522
|
-
STYLE: Add `:host([status="pinned"]) .unpinned` style
|
|
523
|
-
GESTURE: `Gesture.CLICK` exists -> FAIL/PASS
|
|
524
|
-
COMPOSITION: `pin.elements.icon` is a cached reference to the `div` with class `icon` -> FAIL/PASS
|
|
525
|
-
GESTURE: `Gesture.CLICK` on `div` with class `icon` toggle `pin.status` from `Status.UNPINNED` to `Status.PINNED` and visa versa -> FAIL/PASS
|
|
526
|
-
DOCUMENTATION: Generate API Docs
|
|
527
|
-
FEATURE: Pin and Unpin
|
|
528
|
-
```
|
|
529
|
-
|
|
530
|
-
How the policy is applied:
|
|
531
|
-
|
|
532
|
-
- Only the remaining `COMPOSITION` part (`svg.pinned`) is added at the start — just the part this feature requires
|
|
533
|
-
- `STATE` enum values tested in source-declaration order: `Status.PINNED` before `Status.UNPINNED`
|
|
534
|
-
- `STATE` omits null normalization because `status` does not normalize `null`
|
|
535
|
-
- `STATE` includes the attribute-default pair (`status attribute exists`, `status attribute is Status.UNPINNED`) because `status` is compulsory and always written to the DOM
|
|
536
|
-
- `OPERATION` metadata for `PIN` and `UNPIN` batched together before their methods; `TOGGLE` introduced as a separate semantic group with its own metadata-then-method-then-behavior sequence after the paired operations
|
|
537
|
-
- Each event has three behavioral commits: setter existence, handler trigger, payload contract
|
|
538
|
-
- State-driven `STYLE` for pinned/unpinned visual states split per selector — complementary states separated
|
|
539
|
-
- `GESTURE` follows `STYLE`
|
|
540
|
-
- `COMPOSITION` cached reference inserted just-in-time between `GESTURE` metadata and `GESTURE` behavior
|
|
541
|
-
- Gesture behavior commit describes the target by selector (`\`div\` with class \`icon\``)
|
|
542
|
-
- `DOCUMENTATION` → `FEATURE` close the cycle
|
|
543
|
-
|
|
544
|
-
## Feature: Hover Responsiveness
|
|
545
|
-
|
|
546
|
-
Branch: `feature/hover-responsiveness`
|
|
547
|
-
|
|
548
|
-
CSS-only feature — collapsed cycle:
|
|
549
|
-
|
|
550
|
-
```
|
|
551
|
-
STYLE: Add `@media (hover: hover) .icon:hover svg` style
|
|
552
|
-
STYLE: Add `@media (hover: hover) :host([status="unpinned"]) .icon:hover` style
|
|
553
|
-
STYLE: Add `@media (hover: hover) :host([status="pinned"]) .icon:hover` style
|
|
554
|
-
FEATURE: Hover Responsiveness
|
|
555
|
-
```
|
|
556
|
-
|
|
557
|
-
## Feature: Theme Compatibility
|
|
558
|
-
|
|
559
|
-
Branch: `feature/theme-compatibility`
|
|
560
|
-
|
|
561
|
-
CSS-only feature — collapsed cycle:
|
|
562
|
-
|
|
563
|
-
```
|
|
564
|
-
STYLE: Add `@media (prefers-color-scheme: dark) :host` style
|
|
565
|
-
FEATURE: Theme Compatibility
|
|
566
|
-
```
|
|
567
|
-
|
|
568
|
-
---
|
|
569
|
-
|
|
570
|
-
# Policy Checklist
|
|
571
|
-
|
|
572
|
-
A compliant feature branch must:
|
|
573
|
-
|
|
574
|
-
1. Be developed on an isolated feature branch created from main.
|
|
575
|
-
2. Begin every commit message with a canonical prefix from the defined vocabulary (with the sole exceptions of `Initial commit` and version-tag commits).
|
|
576
|
-
3. Use exactly one prefix per commit.
|
|
577
|
-
4. Introduce every behavioral change as a FAIL/PASS commit pair.
|
|
578
|
-
5. Place the FAIL commit immediately before its PASS commit with no commits between them.
|
|
579
|
-
6. Follow the policy-ordered development sequence: `COMPOSITION → STATE → VALIDATION → OPERATION → EVENT → STYLE → GESTURE → DOCUMENTATION → FEATURE`.
|
|
580
|
-
7. Test `COMPOSITION` parts in outside-in structural order using `contains` in the description.
|
|
581
|
-
8. Introduce only the `COMPOSITION` parts required by the current feature.
|
|
582
|
-
9. Follow the fixed coverage sequence for each `STATE` property: metadata → type → enum values (source-declaration order) → getter → default → [attribute default] → setter → mutation → [null normalization] → state→attribute sync → attribute→state sync.
|
|
583
|
-
10. Include attribute-default `STATE` tests only when the attribute is compulsory.
|
|
584
|
-
11. Include null-normalization `STATE` tests only when the setter contains normalization logic.
|
|
585
|
-
12. Use the verb `updates state` and `updates attribute` for mutation and sync commits — not `persists` or `sets`.
|
|
586
|
-
13. Place `VALIDATION` commits after the `STATE` body and before `OPERATION`. Include three pairs: method existence, method error, and setter-level boundary — all under the `VALIDATION:` prefix.
|
|
587
|
-
14. Batch `OPERATION` metadata constants per semantic group, before any method or behavior commits in that group. Composite operations such as `toggle` form their own semantic group.
|
|
588
|
-
15. Use `Invoke` as the uniform verb for all `OPERATION` behavior commits.
|
|
589
|
-
16. Include a precondition in `OPERATION` behavior commits only when the operation requires a specific prior state.
|
|
590
|
-
17. Use `from X to Y and visa versa` for bidirectional `OPERATION` and `GESTURE` behavior commits.
|
|
591
|
-
18. Batch `EVENT` metadata constants per semantic group, before any setter, handler, or payload commits in that group.
|
|
592
|
-
19. Use three commits per event: setter existence, handler trigger (`listener is called when ... changes from X to Y`), and payload (`When ... listener is called then it is called with ...`).
|
|
593
|
-
20. Place state-driven `STYLE` commits after `EVENT` and before `GESTURE`.
|
|
594
|
-
21. Place base element `STYLE` commits just-in-time after the `COMPOSITION` parts they apply to are established.
|
|
595
|
-
22. Use `Define` for base element styles and `Add` for state-driven, gesture-driven, media-query, and extension styles.
|
|
596
|
-
23. Commit one selector per `STYLE` commit. Separate complementary visual states into distinct `STYLE` commits.
|
|
597
|
-
24. Place the `COMPOSITION` cached reference commit between `GESTURE` metadata and `GESTURE` behavior.
|
|
598
|
-
25. Describe `GESTURE` behavior targets by selector phrasing (`\`div\` with class \`icon\``), not by cached reference name.
|
|
599
|
-
26. Close every behavioral feature with `DOCUMENTATION → FEATURE`.
|
|
600
|
-
27. Follow the collapsed cycle (`STYLE → FEATURE`) for CSS-only features.
|
|
601
|
-
28. Use the `FEATURE` commit only as a boundary marker — no new behavior, tests, or styles.
|
|
602
|
-
29. Add `CONFIG` and `COMPOSITION` parts just-in-time at the earliest point they are needed.
|
|
603
|
-
30. Wrap all code references in backticks in commit message descriptions.
|
|
604
|
-
31. Commit test naming convention corrections as standalone `TESTING: Update naming convention` commits, never bundled with behavioral changes.
|
|
605
|
-
32. Use the domain prefix for refactor commits with `refactor` in the message body; do not use a FAIL/PASS pair.
|
|
606
|
-
33. Use `SPECIFICATIONS` for component specification commits.
|
|
607
|
-
34. Use `TYPES` for TypeScript type changes independent of runtime behavior.
|
|
608
|
-
35. Verify every commit message before pushing — prefix present, code references backticked, state values and event names correct, no typos in the feature name.
|
|
1
|
+
# Workflow
|
|
2
|
+
|
|
3
|
+
This document defines the **workflow policy** for component development. It specifies how implementation proceeds from feature branch creation through to merge — the sequence of commits within a feature, the ordering of concerns across the policy hierarchy, the structure of every commit message, and the conventions that govern the entire development flow.
|
|
4
|
+
|
|
5
|
+
This document is intentionally **workflow** only, but part of a set of implementation policies which together define the policies which should be followed to transform specifications into an implementation which is predictable, maintainable, and production-ready at scale.
|
|
6
|
+
|
|
7
|
+
Testing patterns, unit test structure, and TDD discipline are covered in a separate testing policy.
|
|
8
|
+
|
|
9
|
+
---
|
|
10
|
+
|
|
11
|
+
# How to Read This Policy
|
|
12
|
+
|
|
13
|
+
Rules in this policy fall into three strengths. Read each rule with its strength in mind:
|
|
14
|
+
|
|
15
|
+
- **Principles** are non-negotiable. They derive from framework correctness or TDD discipline and hold for every component. A conflict with a principle is a defect.
|
|
16
|
+
- **Templates** are invariant skeletons with explicit variation points. The skeleton applies to every component; specific steps activate or deactivate based on the component's declared classification (e.g., attribute-backed vs non-attribute, readonly vs writable, payload-bearing vs payload-free). Variation is part of the template, not a deviation from it.
|
|
17
|
+
- **Conventions** are canonical defaults that keep implementations readable across components. A second component may deviate when its shape genuinely warrants it; the deviation should be explained at review time, not forbidden by the policy.
|
|
18
|
+
|
|
19
|
+
When applying this policy to a new component, classify each rule before reconciling conflicts. A conflict with a principle is a defect; a conflict with a template means a variation point needs to be made explicit; a conflict with a convention is a documented deviation.
|
|
20
|
+
|
|
21
|
+
---
|
|
22
|
+
|
|
23
|
+
# 1) Every Feature Is Developed on an Isolated Branch
|
|
24
|
+
|
|
25
|
+
Each new feature is developed in isolation on a dedicated feature branch created from main.
|
|
26
|
+
|
|
27
|
+
A feature branch:
|
|
28
|
+
|
|
29
|
+
- is created from the main branch before any work begins
|
|
30
|
+
- contains all commits belonging to a single feature cycle
|
|
31
|
+
- is merged into main via a `FEATURE` commit
|
|
32
|
+
|
|
33
|
+
The `FEATURE` commit is always the last commit on the feature branch. It marks the boundary of the cycle and the merge point back into main.
|
|
34
|
+
|
|
35
|
+
No behavioral work is committed directly to main. Direct commits to main are limited to non-feature concerns such as `PACKAGE`, `TYPES`, post-merge naming corrections, version bumps, or refactors.
|
|
36
|
+
|
|
37
|
+
# 2) Every Commit Must Use a Canonical Prefix
|
|
38
|
+
|
|
39
|
+
Every commit message must begin with a canonical prefix from the following fixed vocabulary:
|
|
40
|
+
|
|
41
|
+
| Prefix | Domain |
|
|
42
|
+
| ---------------- | ------------------------------------------------------------------------------------------------------------- |
|
|
43
|
+
| `COMPOSITION` | DOM structure and cached element references |
|
|
44
|
+
| `STATE` | State contract — metadata, type, enum values, getter, setter, mutation, attribute sync |
|
|
45
|
+
| `VALIDATION` | Input validation — `Validate` class methods and error boundaries (including setter-level invalid-value tests) |
|
|
46
|
+
| `OPERATION` | Public methods — metadata, existence, and behavioral outcome |
|
|
47
|
+
| `EVENT` | Event contract — metadata, setter, handler behavior, and payload |
|
|
48
|
+
| `GESTURE` | Interaction wiring — metadata and behavioral outcome |
|
|
49
|
+
| `DATA` | Component-owned data contract |
|
|
50
|
+
| `STYLE` | CSS rules |
|
|
51
|
+
| `SPECIFICATIONS` | Component specification document |
|
|
52
|
+
| `TESTING` | Test vocabulary corrections |
|
|
53
|
+
| `DOCUMENTATION` | Generated API documentation |
|
|
54
|
+
| `FEATURE` | Feature boundary marker |
|
|
55
|
+
| `PACKAGE` | Package metadata |
|
|
56
|
+
| `CONFIG` | Test and build infrastructure |
|
|
57
|
+
| `TYPES` | TypeScript type definitions independent of runtime behavior |
|
|
58
|
+
|
|
59
|
+
The prefix identifies the concern domain of the commit. It must always be uppercase and must be the first token in the commit message, followed by `: `.
|
|
60
|
+
|
|
61
|
+
Every commit must use exactly one prefix. The `Initial commit` and version-tag commits (e.g. `0.1.0`) are the only permitted exceptions.
|
|
62
|
+
|
|
63
|
+
`CONFIG`, `TYPES`, `TESTING`, and refactor prefixes are reserved for their respective concerns and may not appear in every feature cycle.
|
|
64
|
+
|
|
65
|
+
# 3) Every Behavioral Change Requires a FAIL/PASS Commit Pair
|
|
66
|
+
|
|
67
|
+
Every behavioral change — `COMPOSITION`, `STATE`, `VALIDATION`, `OPERATION`, `EVENT`, `GESTURE`, or `DATA` — must be introduced as a FAIL/PASS commit pair.
|
|
68
|
+
|
|
69
|
+
Structure:
|
|
70
|
+
|
|
71
|
+
- The `FAIL` commit introduces the test that describes the expected behavior. The test must fail at the point of commit.
|
|
72
|
+
- The `PASS` commit introduces the minimum implementation required to make the test pass. The test must pass at the point of commit.
|
|
73
|
+
|
|
74
|
+
Rules:
|
|
75
|
+
|
|
76
|
+
- The `FAIL` commit must precede the `PASS` commit.
|
|
77
|
+
- The `FAIL` and `PASS` commits must always be adjacent — no other commits may appear between them.
|
|
78
|
+
- Every `FAIL` must have a corresponding `PASS`.
|
|
79
|
+
- No behavioral implementation may be committed without a preceding `FAIL` commit.
|
|
80
|
+
|
|
81
|
+
`STYLE`, `SPECIFICATIONS`, `DOCUMENTATION`, `FEATURE`, `PACKAGE`, `CONFIG`, `TYPES`, `TESTING`, and refactor commits do not require a FAIL/PASS pair.
|
|
82
|
+
|
|
83
|
+
# 4) The Feature Development Sequence Is Policy-Ordered
|
|
84
|
+
|
|
85
|
+
Within a behavioral feature, commits must follow this canonical order:
|
|
86
|
+
|
|
87
|
+
```
|
|
88
|
+
COMPOSITION
|
|
89
|
+
STATE
|
|
90
|
+
VALIDATION
|
|
91
|
+
OPERATION
|
|
92
|
+
EVENT
|
|
93
|
+
STYLE (state-driven)
|
|
94
|
+
GESTURE
|
|
95
|
+
DOCUMENTATION
|
|
96
|
+
FEATURE
|
|
97
|
+
```
|
|
98
|
+
|
|
99
|
+
This ordering reflects the dependency structure of the implementation. Each layer depends on the layers above it, so work proceeds top-down through the policy hierarchy.
|
|
100
|
+
|
|
101
|
+
`STYLE` for state-driven rules appears after `EVENT` because the visual states it reflects are fully established only once the state and event contracts are in place.
|
|
102
|
+
|
|
103
|
+
`GESTURE` follows state-driven `STYLE` because gesture wiring depends on cached element references, which in turn depend on the composition being final.
|
|
104
|
+
|
|
105
|
+
Deviations from this order are permitted only under the just-in-time rule (see Section 14).
|
|
106
|
+
|
|
107
|
+
# 5) COMPOSITION Commits Follow an Outside-In Structural Order
|
|
108
|
+
|
|
109
|
+
`COMPOSITION` commits introduce structural DOM parts and cached element references.
|
|
110
|
+
|
|
111
|
+
Within a feature, `COMPOSITION` commits must precede all other behavioral commits, except for the cached-reference commit which is placed just-in-time between `GESTURE` metadata and `GESTURE` behavior (see Section 10).
|
|
112
|
+
|
|
113
|
+
Ordering rules:
|
|
114
|
+
|
|
115
|
+
- Container elements are introduced before their children.
|
|
116
|
+
- Parent-child structural relationships use the word `contains` in the commit description.
|
|
117
|
+
- Cached element references are a `COMPOSITION` concern and must use the `COMPOSITION` prefix.
|
|
118
|
+
|
|
119
|
+
Example:
|
|
120
|
+
|
|
121
|
+
```
|
|
122
|
+
COMPOSITION: `pin.root` contains `div.icon` -> FAIL
|
|
123
|
+
COMPOSITION: `pin.root` contains `div.icon` -> PASS
|
|
124
|
+
COMPOSITION: `div.icon` contains `svg.unpinned` -> FAIL
|
|
125
|
+
COMPOSITION: `div.icon` contains `svg.unpinned` -> PASS
|
|
126
|
+
```
|
|
127
|
+
|
|
128
|
+
Structural parts are added incrementally across features. Only the parts required by the current feature are introduced in that feature's cycle.
|
|
129
|
+
|
|
130
|
+
# 6) STATE Commits Follow a Fixed Coverage Sequence
|
|
131
|
+
|
|
132
|
+
For each state property introduced in a feature, `STATE` commits must follow this order:
|
|
133
|
+
|
|
134
|
+
1. **Metadata pair** — `Attributes.X` and `State.X` tested together in one commit
|
|
135
|
+
2. **Type** — the value-domain type for the state property (e.g. `Visibility`, `Status`)
|
|
136
|
+
3. **Enum values** — tested in source-declaration order (the order they appear in `<component>.meta.ts`)
|
|
137
|
+
4. **Getter existence**
|
|
138
|
+
5. **Default value** — the internal runtime default returned by the getter
|
|
139
|
+
6. **Attribute default** — only when the attribute is compulsory and always written to the DOM (two commits: `<attribute> attribute exists` and `<attribute> attribute is <default>`)
|
|
140
|
+
7. **Setter existence**
|
|
141
|
+
8. **Mutation persists** — setting the value via the setter updates the internal state
|
|
142
|
+
9. **Null normalization** — only when the setter normalizes `null` to a default value
|
|
143
|
+
10. **Attribute synchronization (state → attribute)** — setter assignment updates the DOM attribute
|
|
144
|
+
11. **Attribute synchronization (attribute → state)** — `setAttribute` drives the getter
|
|
145
|
+
|
|
146
|
+
Steps 6 and 9 are conditional:
|
|
147
|
+
|
|
148
|
+
- Include attribute-default only when the attribute is compulsory and always present in the DOM.
|
|
149
|
+
- Include null normalization only when the setter contains normalization logic (`value ?? default`).
|
|
150
|
+
|
|
151
|
+
The validation-boundary commit — `set <component>.<state> to invalid throws error` — is logically a STATE-layer assertion but is committed under the `VALIDATION:` prefix. It appears immediately after the `VALIDATION` method-existence and method-error commits (see Section 7).
|
|
152
|
+
|
|
153
|
+
# 7) VALIDATION Commits Are Placed Between STATE and OPERATION
|
|
154
|
+
|
|
155
|
+
`VALIDATION` commits introduce the `Validate` class methods that enforce input boundaries.
|
|
156
|
+
|
|
157
|
+
Within a feature, `VALIDATION` commits appear after the main body of `STATE` commits and before `OPERATION` commits.
|
|
158
|
+
|
|
159
|
+
The validation block has three commit pairs:
|
|
160
|
+
|
|
161
|
+
1. `VALIDATION: \`Validate.x\` static method exists`— verifies the method on the`Validate` class
|
|
162
|
+
2. `VALIDATION: \`Validate.x(value)\` throws error if \`value\` is \`invalid\`` — verifies the validator-level error
|
|
163
|
+
3. `VALIDATION: set \`pin.x\` to \`invalid\` throws error` — verifies that the setter propagates the validator error
|
|
164
|
+
|
|
165
|
+
The third commit is the setter-level validation boundary. Although it asserts on the STATE setter, it remains conceptually part of the validation contract and uses the `VALIDATION:` prefix.
|
|
166
|
+
|
|
167
|
+
Sequence:
|
|
168
|
+
|
|
169
|
+
```
|
|
170
|
+
STATE: ... (main body, steps 1-11 excluding the boundary)
|
|
171
|
+
VALIDATION: `Validate.x` static method exists -> FAIL/PASS
|
|
172
|
+
VALIDATION: `Validate.x(value)` throws error if `value` is `invalid` -> FAIL/PASS
|
|
173
|
+
VALIDATION: set `pin.x` to `invalid` throws error -> FAIL/PASS
|
|
174
|
+
OPERATION: ...
|
|
175
|
+
```
|
|
176
|
+
|
|
177
|
+
# 8) OPERATION Commits Follow a Semantic-Group Batch-Then-Sequence Pattern
|
|
178
|
+
|
|
179
|
+
Operations are grouped semantically — paired operations such as `hide`/`show` or `pin`/`unpin` belong to one group; composite or derived operations such as `toggle` belong to a separate group.
|
|
180
|
+
|
|
181
|
+
For each semantic group:
|
|
182
|
+
|
|
183
|
+
1. Test all metadata constants for the group first — all `Operation.X exists` commits for the group appear before any method or behavior commit in that group.
|
|
184
|
+
2. For each operation in the group: method exists → `Invoke` behavior.
|
|
185
|
+
|
|
186
|
+
Behavior commit rules:
|
|
187
|
+
|
|
188
|
+
- Use `Invoke` as the uniform verb for all operation behavior commits.
|
|
189
|
+
- Include a precondition in the commit description when the operation is only semantically meaningful from a specific prior state (e.g., `show` requires the component to already be hidden).
|
|
190
|
+
- Omit the precondition when the operation can be meaningfully called from the default state.
|
|
191
|
+
- When an operation covers a bidirectional transition, state both directions in one commit description using `from X to Y and visa versa`.
|
|
192
|
+
|
|
193
|
+
Example — paired group followed by composite group:
|
|
194
|
+
|
|
195
|
+
```
|
|
196
|
+
OPERATION: `Operation.PIN` exists -> FAIL/PASS
|
|
197
|
+
OPERATION: `Operation.UNPIN` exists -> FAIL/PASS
|
|
198
|
+
OPERATION: `pin.pin` method exists -> FAIL/PASS
|
|
199
|
+
OPERATION: Invoke `pin.pin` sets `pin.status` to `Status.PINNED` -> FAIL/PASS
|
|
200
|
+
OPERATION: `pin.unpin` method exists -> FAIL/PASS
|
|
201
|
+
OPERATION: Invoke `pin.unpin` when `pin.status` is `Status.PINNED` sets `pin.status` to `Status.UNPINNED` -> FAIL/PASS
|
|
202
|
+
OPERATION: `Operation.TOGGLE` exists -> FAIL/PASS
|
|
203
|
+
OPERATION: `pin.toggle` method exists -> FAIL/PASS
|
|
204
|
+
OPERATION: Invoke `pin.toggle` toggles `pin.status` from `Status.UNPINNED` to `Status.PINNED` and visa versa -> FAIL/PASS
|
|
205
|
+
```
|
|
206
|
+
|
|
207
|
+
# 9) EVENT Commits Follow a Three-Per-Event Pattern
|
|
208
|
+
|
|
209
|
+
For each semantic event group, `EVENT` commits follow this pattern:
|
|
210
|
+
|
|
211
|
+
1. Test all event metadata constants for the group first — all `Event.ON_X exists` commits for the group appear before any setter, handler, or payload commit.
|
|
212
|
+
2. For each event in the group, three behavioral commit pairs:
|
|
213
|
+
- **Setter existence** — `pin.on{x} setter exists`
|
|
214
|
+
- **Handler trigger** — `pin.on{x} listener is called when pin.<state> changes from <Before> to <After>`
|
|
215
|
+
- **Payload contract** — `When pin.on{x} listener is called then it is called with \`{ detail: { ... } }\``
|
|
216
|
+
|
|
217
|
+
Handler-trigger commit rules:
|
|
218
|
+
|
|
219
|
+
- Describe the full state transition using `from X to Y` phrasing.
|
|
220
|
+
- The direction must be explicit in the commit description — the before and after state values must both be named.
|
|
221
|
+
|
|
222
|
+
Example:
|
|
223
|
+
|
|
224
|
+
```
|
|
225
|
+
EVENT: `Event.ON_HIDE` exists -> FAIL/PASS
|
|
226
|
+
EVENT: `Event.ON_SHOW` exists -> FAIL/PASS
|
|
227
|
+
EVENT: `pin.onhide` setter exists -> FAIL/PASS
|
|
228
|
+
EVENT: `pin.onhide` listener is called when `pin.visibility` changes from `Visibility.VISIBLE` to `Visibility.HIDDEN` -> FAIL/PASS
|
|
229
|
+
EVENT: When `pin.onhide` listener is called then it is called with `{ detail: { visibility: Visibility.HIDDEN } }` -> FAIL/PASS
|
|
230
|
+
EVENT: `pin.onshow` setter exists -> FAIL/PASS
|
|
231
|
+
EVENT: `pin.onshow` listener is called when `pin.visibility` changes from `Visibility.HIDDEN` to `Visibility.VISIBLE` -> FAIL/PASS
|
|
232
|
+
EVENT: When `pin.onshow` listener is called then it is called with `{ detail: { visibility: Visibility.VISIBLE } }` -> FAIL/PASS
|
|
233
|
+
```
|
|
234
|
+
|
|
235
|
+
# 10) GESTURE Commits Bracket the COMPOSITION Cached Reference
|
|
236
|
+
|
|
237
|
+
`GESTURE` commits introduce interaction wiring.
|
|
238
|
+
|
|
239
|
+
Within a feature, `GESTURE` commits appear after `STYLE` and before `DOCUMENTATION`.
|
|
240
|
+
|
|
241
|
+
The `COMPOSITION` cached element reference commit is placed just-in-time between the gesture metadata commit and the gesture behavior commit — not at the start of the feature cycle — because the cache is the direct prerequisite for listener wiring and not needed before that point.
|
|
242
|
+
|
|
243
|
+
Order:
|
|
244
|
+
|
|
245
|
+
```
|
|
246
|
+
GESTURE: `Gesture.X` exists -> FAIL/PASS
|
|
247
|
+
COMPOSITION: `pin.elements.x` is a cached reference to the `<element>` with class `<class>` -> FAIL/PASS
|
|
248
|
+
GESTURE: `Gesture.X` on `<element>` with class `<class>` <behavior> -> FAIL/PASS
|
|
249
|
+
```
|
|
250
|
+
|
|
251
|
+
GESTURE behavior descriptions identify the realized DOM element using selector phrasing (`\`div\` with class \`icon\``), not by the cached reference name. This keeps the commit description aligned with how the test exercises the interaction — by querying the realized DOM.
|
|
252
|
+
|
|
253
|
+
When a gesture behavior covers a bidirectional toggle, both directions are stated in a single behavior commit description using `from X to Y and visa versa`.
|
|
254
|
+
|
|
255
|
+
Example:
|
|
256
|
+
|
|
257
|
+
```
|
|
258
|
+
GESTURE: `Gesture.CLICK` exists -> FAIL/PASS
|
|
259
|
+
COMPOSITION: `pin.elements.icon` is a cached reference to the `div` with class `icon` -> FAIL/PASS
|
|
260
|
+
GESTURE: `Gesture.CLICK` on `div` with class `icon` toggle `pin.status` from `Status.UNPINNED` to `Status.PINNED` and visa versa -> FAIL/PASS
|
|
261
|
+
```
|
|
262
|
+
|
|
263
|
+
# 11) STYLE Commits Are Placed at the Point of Visual Readiness
|
|
264
|
+
|
|
265
|
+
`STYLE` commits are placed at the earliest point in the feature cycle where the elements and state they depend on are fully established.
|
|
266
|
+
|
|
267
|
+
Two placement rules apply:
|
|
268
|
+
|
|
269
|
+
- **Base element styles** — styles that apply to DOM elements with no state dependency may be committed immediately after the `COMPOSITION` commits that establish those elements. These can appear before `STATE` commits if the elements already exist.
|
|
270
|
+
- **State-driven styles** — styles whose selectors depend on state attributes (`:host([attribute="value"])`) must appear after all `EVENT` commits for that state, since the full state contract must be in place before its visual representation is added.
|
|
271
|
+
|
|
272
|
+
All `STYLE` commits must appear before `DOCUMENTATION`.
|
|
273
|
+
|
|
274
|
+
`STYLE` commit rules:
|
|
275
|
+
|
|
276
|
+
- `STYLE` commits never have a FAIL/PASS pair.
|
|
277
|
+
- Use `Define` for base element styles — rules with no state dependency that establish a default appearance for a structural part.
|
|
278
|
+
- Use `Add` for state-driven styles, media-query styles, hover/focus styles, and any style that extends an existing visual contract.
|
|
279
|
+
- One selector (or one logically-grouped selector list bound to a single rule) per commit. Complementary visual states (e.g., pinned rules and unpinned rules) are separated into distinct `STYLE` commits.
|
|
280
|
+
- `@media` blocks are committed per rule inside the block — the media query is part of the commit description, not a wrapper around multiple selector commits.
|
|
281
|
+
|
|
282
|
+
Examples:
|
|
283
|
+
|
|
284
|
+
```
|
|
285
|
+
STYLE: Define `svg` style
|
|
286
|
+
STYLE: Define `.icon` style
|
|
287
|
+
STYLE: Add `:host([visibility="hidden"])` style
|
|
288
|
+
STYLE: Add `:host([status="pinned"]) .icon` style
|
|
289
|
+
STYLE: Add `@media (hover: hover) .icon:hover svg` style
|
|
290
|
+
STYLE: Add `@media (prefers-color-scheme: dark) :host` style
|
|
291
|
+
```
|
|
292
|
+
|
|
293
|
+
# 12) CSS-Only Features Follow a Collapsed Sequence
|
|
294
|
+
|
|
295
|
+
When a feature introduces only visual changes with no new behavioral contract, it follows a collapsed cycle with no `FAIL/PASS` pairs and no `DOCUMENTATION` commit:
|
|
296
|
+
|
|
297
|
+
```
|
|
298
|
+
STYLE
|
|
299
|
+
FEATURE
|
|
300
|
+
```
|
|
301
|
+
|
|
302
|
+
This applies to features whose entire change is within CSS — such as hover responsiveness or dark mode support. No behavioral commit sequence is required.
|
|
303
|
+
|
|
304
|
+
# 13) DOCUMENTATION Closes Every Behavioral Feature
|
|
305
|
+
|
|
306
|
+
After all `FAIL/PASS` pairs and `STYLE` commits are complete, the behavioral feature cycle closes with:
|
|
307
|
+
|
|
308
|
+
```
|
|
309
|
+
DOCUMENTATION
|
|
310
|
+
FEATURE
|
|
311
|
+
```
|
|
312
|
+
|
|
313
|
+
Rules:
|
|
314
|
+
|
|
315
|
+
- `DOCUMENTATION` is always generated after implementation is complete. It must not precede any behavioral commits.
|
|
316
|
+
- `FEATURE` follows `DOCUMENTATION`. It is the boundary commit for the branch.
|
|
317
|
+
|
|
318
|
+
# 14) Just-in-Time Addition of Infrastructure and Composition
|
|
319
|
+
|
|
320
|
+
Not all `COMPOSITION` parts and `CONFIG` infrastructure are added at the start of a feature cycle. They are added at the earliest point in the sequence where they become necessary.
|
|
321
|
+
|
|
322
|
+
Rules:
|
|
323
|
+
|
|
324
|
+
- A `CONFIG` commit introducing test infrastructure appears immediately before the first commit that requires it.
|
|
325
|
+
- A `COMPOSITION` commit for a structural part appears at the start of the feature cycle that first requires that part.
|
|
326
|
+
- A `COMPOSITION` cached reference commit appears between `GESTURE` metadata and `GESTURE` behavior — not at the start of the cycle — because the cache is the direct prerequisite for listener wiring.
|
|
327
|
+
|
|
328
|
+
The just-in-time principle avoids speculative additions and keeps every commit traceable to the specific need that drove it.
|
|
329
|
+
|
|
330
|
+
# 15) Commit Messages Must Follow a Canonical Format
|
|
331
|
+
|
|
332
|
+
Every commit message must conform to one of the following formats:
|
|
333
|
+
|
|
334
|
+
**Behavioral commit (FAIL):**
|
|
335
|
+
|
|
336
|
+
```
|
|
337
|
+
{PREFIX}: {description} -> FAIL
|
|
338
|
+
```
|
|
339
|
+
|
|
340
|
+
**Behavioral commit (PASS):**
|
|
341
|
+
|
|
342
|
+
```
|
|
343
|
+
{PREFIX}: {description} -> PASS
|
|
344
|
+
```
|
|
345
|
+
|
|
346
|
+
**Non-behavioral commit:**
|
|
347
|
+
|
|
348
|
+
```
|
|
349
|
+
{PREFIX}: {description}
|
|
350
|
+
```
|
|
351
|
+
|
|
352
|
+
Formatting rules within descriptions:
|
|
353
|
+
|
|
354
|
+
- Code references — types, values, methods, attributes — must be wrapped in backticks.
|
|
355
|
+
- Structural relationships use `contains` as the verb.
|
|
356
|
+
- Operation behavior commits begin with `Invoke`.
|
|
357
|
+
- Event handler behavior commits include `from X to Y` for state transitions.
|
|
358
|
+
- Event payload commits begin with `When ... listener is called then it is called with` followed by the backticked `{ detail: { ... } }` payload.
|
|
359
|
+
- Gesture behavior commits identify the target element by selector phrasing (`\`div\` with class \`icon\``), not by cached reference name.
|
|
360
|
+
- Bidirectional toggle behaviors state both directions: `from X to Y and visa versa`.
|
|
361
|
+
- Preconditions use `when {subject} is {value}` phrasing.
|
|
362
|
+
|
|
363
|
+
# 16) Commit-Message Hygiene Is a Reviewable Concern
|
|
364
|
+
|
|
365
|
+
Commit messages are part of the historical record and must be reviewed with the same discipline as code.
|
|
366
|
+
|
|
367
|
+
Policy:
|
|
368
|
+
|
|
369
|
+
- The `{PREFIX}: ` token must not be omitted from a behavioral commit.
|
|
370
|
+
- State values, event names, and operation names referenced in the description must match the values actually exercised by the test and the implementation.
|
|
371
|
+
- Typos in the feature name (the `FEATURE: ` boundary commit) must be corrected before merge.
|
|
372
|
+
- A commit message that misnames a state value, event handler, or operation is a defect even if the test and implementation are correct.
|
|
373
|
+
|
|
374
|
+
A standalone `TESTING: Update naming convention` commit may be used to correct historical commit-message drift only when the drift is in the test vocabulary itself. Correcting an isolated commit-message typo retroactively is not in scope — the discipline is to catch it before merge.
|
|
375
|
+
|
|
376
|
+
# 17) Naming Convention Corrections Are Standalone Commits
|
|
377
|
+
|
|
378
|
+
When test vocabulary drifts and requires correction, the correction is committed as a standalone `TESTING: Update naming convention` commit.
|
|
379
|
+
|
|
380
|
+
This commit must not be bundled with behavioral changes. It is standalone.
|
|
381
|
+
|
|
382
|
+
# 18) Refactor Commits Use the Domain Prefix
|
|
383
|
+
|
|
384
|
+
When implementation is refactored without changing behavior:
|
|
385
|
+
|
|
386
|
+
- Use the domain prefix of the area being refactored (`STATE: refactor ...`, not a separate `REFACTOR` prefix)
|
|
387
|
+
- Include `refactor` as a verb in the commit message body
|
|
388
|
+
- Do not use a FAIL/PASS pair — behavior does not change under a refactor
|
|
389
|
+
|
|
390
|
+
# 19) Non-Behavioral Prefixes Have Dedicated Scopes
|
|
391
|
+
|
|
392
|
+
Non-behavioral concerns use their own prefixes and are not embedded in behavioral commit sequences:
|
|
393
|
+
|
|
394
|
+
- `SPECIFICATIONS` — Component specification document. Appears at project initialization when the canonical specification is added or when the specification is updated independently of implementation.
|
|
395
|
+
- `TYPES` — TypeScript type changes independent of runtime behavior. May appear post-feature when a type correction is identified after merge.
|
|
396
|
+
- `PACKAGE` — Package metadata. Appears at project initialization or when package configuration changes.
|
|
397
|
+
- `CONFIG` — Test and build infrastructure. Added just-in-time when a new capability is needed.
|
|
398
|
+
|
|
399
|
+
---
|
|
400
|
+
|
|
401
|
+
# Policy Applied to Specification
|
|
402
|
+
|
|
403
|
+
The Pin component was developed across four feature cycles. The following shows how the workflow policy maps onto each cycle.
|
|
404
|
+
|
|
405
|
+
## Project Initialization
|
|
406
|
+
|
|
407
|
+
Direct commits to main:
|
|
408
|
+
|
|
409
|
+
```
|
|
410
|
+
Initial commit
|
|
411
|
+
SPECIFICATIONS: Add `pin.specifications.json`
|
|
412
|
+
PACKAGE: Update package metadata
|
|
413
|
+
DOCUMENTATION: Generate API Docs
|
|
414
|
+
```
|
|
415
|
+
|
|
416
|
+
## Feature: Visibility Control
|
|
417
|
+
|
|
418
|
+
Branch: `feature/visibility-control`
|
|
419
|
+
|
|
420
|
+
Behavioral feature — full cycle:
|
|
421
|
+
|
|
422
|
+
```
|
|
423
|
+
COMPOSITION: `pin.root` contains `div.icon` -> FAIL/PASS
|
|
424
|
+
COMPOSITION: `div.icon` contains `svg.unpinned` -> FAIL/PASS
|
|
425
|
+
STYLE: Define `.icon` style
|
|
426
|
+
STYLE: Define `svg` style
|
|
427
|
+
STATE: `Attributes.VISIBILITY` and `State.VISIBILITY` exists -> FAIL/PASS
|
|
428
|
+
STATE: `Visibility` exists -> FAIL/PASS
|
|
429
|
+
STATE: `Visibility.VISIBLE` exists -> FAIL/PASS
|
|
430
|
+
STATE: `Visibility.HIDDEN` exists -> FAIL/PASS
|
|
431
|
+
STATE: `pin.visibility` getter exists -> FAIL/PASS
|
|
432
|
+
STATE: `pin.visibility` getter returns `Visibility.VISIBLE` -> FAIL/PASS
|
|
433
|
+
STATE: `pin.visibility` setter exists -> FAIL/PASS
|
|
434
|
+
STATE: set `pin.visibility` to `Visibility.HIDDEN` updates state -> FAIL/PASS
|
|
435
|
+
STATE: set `pin.visibility` to `null` updates state to `Visibility.VISIBLE` -> FAIL/PASS
|
|
436
|
+
STATE: set `pin.visibility` to `Visibility.HIDDEN` updates attribute -> FAIL/PASS
|
|
437
|
+
STATE: set `visibility` attribute to `Visibility.HIDDEN` updates state -> FAIL/PASS
|
|
438
|
+
VALIDATION: `Validate.visibility` static method exists -> FAIL/PASS
|
|
439
|
+
VALIDATION: `Validate.visibility(value)` throws error if `value` is `invalid` -> FAIL/PASS
|
|
440
|
+
VALIDATION: set `pin.visibility` to `invalid` throws error -> FAIL/PASS
|
|
441
|
+
OPERATION: `Operation.HIDE` exists -> FAIL/PASS
|
|
442
|
+
OPERATION: `Operation.SHOW` exists -> FAIL/PASS
|
|
443
|
+
OPERATION: `pin.hide` method exists -> FAIL/PASS
|
|
444
|
+
OPERATION: Invoke `pin.hide` sets `pin.visibility` to `Visibility.HIDDEN` -> FAIL/PASS
|
|
445
|
+
OPERATION: `pin.show` method exists -> FAIL/PASS
|
|
446
|
+
OPERATION: Invoke `pin.show` when `pin.visibility` is `Visibility.HIDDEN` sets `pin.visibility` to `Visibility.VISIBLE` -> FAIL/PASS
|
|
447
|
+
EVENT: `Event.ON_HIDE` exists -> FAIL/PASS
|
|
448
|
+
EVENT: `Event.ON_SHOW` exists -> FAIL/PASS
|
|
449
|
+
EVENT: `pin.onhide` setter exists -> FAIL/PASS
|
|
450
|
+
EVENT: `pin.onhide` listener is called when `pin.visibility` changes from `Visibility.VISIBLE` to `Visibility.HIDDEN` -> FAIL/PASS
|
|
451
|
+
EVENT: When `pin.onhide` listener is called then it is called with `{ detail: { visibility: Visibility.HIDDEN } }` -> FAIL/PASS
|
|
452
|
+
EVENT: `pin.onshow` setter exists -> FAIL/PASS
|
|
453
|
+
EVENT: `pin.onshow` listener is called when `pin.visibility` changes from `Visibility.HIDDEN` to `Visibility.VISIBLE` -> FAIL/PASS
|
|
454
|
+
EVENT: When `pin.onshow` listener is called then it is called with `{ detail: { visibility: Visibility.VISIBLE } }` -> FAIL/PASS
|
|
455
|
+
STYLE: Add `:host([visibility="hidden"])` style
|
|
456
|
+
DOCUMENTATION: Generate API Docs
|
|
457
|
+
FEATURE: Visibility Control
|
|
458
|
+
```
|
|
459
|
+
|
|
460
|
+
How the policy is applied:
|
|
461
|
+
|
|
462
|
+
- `COMPOSITION` parts added outside-in at the start of the cycle
|
|
463
|
+
- Base `STYLE` (`.icon`, `svg`) added just-in-time after the composed elements exist, before `STATE`
|
|
464
|
+
- `STATE` coverage follows the fixed sequence: metadata → type → enum values (source order) → getter → default → setter → mutation → null normalization → state→attribute → attribute→state
|
|
465
|
+
- Enum values tested in source-declaration order: `Visibility.VISIBLE` before `Visibility.HIDDEN`
|
|
466
|
+
- Null normalization included because `visibility` normalizes `null` to `Visibility.VISIBLE`
|
|
467
|
+
- Attribute-default omitted because `visibility` is optional — the attribute is absent when the value is the default
|
|
468
|
+
- `VALIDATION` commits include the method-existence pair, the method-error pair, and the setter-level boundary commit — all under the `VALIDATION:` prefix
|
|
469
|
+
- `OPERATION` metadata (`HIDE`, `SHOW`) batched before any method commits
|
|
470
|
+
- `EVENT` metadata (`ON_HIDE`, `ON_SHOW`) batched before any setter, handler, or payload commits
|
|
471
|
+
- Each event has three behavioral commits: setter existence, handler trigger, payload contract
|
|
472
|
+
- State-driven `STYLE` follows `EVENT`
|
|
473
|
+
- No `GESTURE` in this feature — visibility is controlled imperatively only
|
|
474
|
+
- `DOCUMENTATION` → `FEATURE` close the cycle
|
|
475
|
+
|
|
476
|
+
## Feature: Pin and Unpin
|
|
477
|
+
|
|
478
|
+
Branch: `feature/pin-unpin`
|
|
479
|
+
|
|
480
|
+
Behavioral feature — full cycle:
|
|
481
|
+
|
|
482
|
+
```
|
|
483
|
+
COMPOSITION: `div.icon` contains `svg.pinned` -> FAIL/PASS
|
|
484
|
+
STATE: `Attributes.STATUS` and `State.STATUS` exists -> FAIL/PASS
|
|
485
|
+
STATE: `Status` exists -> FAIL/PASS
|
|
486
|
+
STATE: `Status.PINNED` exists -> FAIL/PASS
|
|
487
|
+
STATE: `Status.UNPINNED` exists -> FAIL/PASS
|
|
488
|
+
STATE: `pin.status` getter exists -> FAIL/PASS
|
|
489
|
+
STATE: `pin.status` getter returns `Status.UNPINNED` -> FAIL/PASS
|
|
490
|
+
STATE: `status` attribute exists -> FAIL/PASS
|
|
491
|
+
STATE: `status` attribute is `Status.UNPINNED` -> FAIL/PASS
|
|
492
|
+
STATE: `pin.status` setter exists -> FAIL/PASS
|
|
493
|
+
STATE: set `pin.status` to `Status.PINNED` updates state -> FAIL/PASS
|
|
494
|
+
STATE: set `pin.status` to `Status.PINNED` updates attribute -> FAIL/PASS
|
|
495
|
+
STATE: set `status` attribute to `Status.PINNED` updates state -> FAIL/PASS
|
|
496
|
+
VALIDATION: `Validate.status` static method exists -> FAIL/PASS
|
|
497
|
+
VALIDATION: `Validate.status(value)` throws error if `value` is `invalid` -> FAIL/PASS
|
|
498
|
+
VALIDATION: set `pin.status` to `invalid` throws error -> FAIL/PASS
|
|
499
|
+
OPERATION: `Operation.PIN` exists -> FAIL/PASS
|
|
500
|
+
OPERATION: `Operation.UNPIN` exists -> FAIL/PASS
|
|
501
|
+
OPERATION: `pin.pin` method exists -> FAIL/PASS
|
|
502
|
+
OPERATION: Invoke `pin.pin` sets `pin.status` to `Status.PINNED` -> FAIL/PASS
|
|
503
|
+
OPERATION: `pin.unpin` method exists -> FAIL/PASS
|
|
504
|
+
OPERATION: Invoke `pin.unpin` when `pin.status` is `Status.PINNED` sets `pin.status` to `Status.UNPINNED` -> FAIL/PASS
|
|
505
|
+
OPERATION: `Operation.TOGGLE` exists -> FAIL/PASS
|
|
506
|
+
OPERATION: `pin.toggle` method exists -> FAIL/PASS
|
|
507
|
+
OPERATION: Invoke `pin.toggle` toggles `pin.status` from `Status.UNPINNED` to `Status.PINNED` and visa versa -> FAIL/PASS
|
|
508
|
+
EVENT: `Event.ON_PIN` exists -> FAIL/PASS
|
|
509
|
+
EVENT: `Event.ON_UNPIN` exists -> FAIL/PASS
|
|
510
|
+
EVENT: `pin.onpin` setter exists -> FAIL/PASS
|
|
511
|
+
EVENT: `pin.onpin` listener is called when `pin.status` changes from `Status.UNPINNED` to `Status.PINNED` -> FAIL/PASS
|
|
512
|
+
EVENT: When `pin.onpin` listener is called then it is called with `{ detail: { status: Status.PINNED } }` -> FAIL/PASS
|
|
513
|
+
EVENT: `pin.onunpin` setter exists -> FAIL/PASS
|
|
514
|
+
EVENT: `pin.onunpin` listener is called when `pin.status` changes from `Status.PINNED` to `Status.UNPINNED` -> FAIL/PASS
|
|
515
|
+
EVENT: When `pin.onunpin` listener is called then it is called with `{ detail: { status: Status.UNPINNED } }` -> FAIL/PASS
|
|
516
|
+
STYLE: Add `.unpinned` style
|
|
517
|
+
STYLE: Add `:host([status="unpinned"]) .pinned` style
|
|
518
|
+
STYLE: Add `:host([status="unpinned"]) .unpinned` style
|
|
519
|
+
STYLE: Add `.pinned` style
|
|
520
|
+
STYLE: Add `:host([status="pinned"]) .icon` style
|
|
521
|
+
STYLE: Add `:host([status="pinned"]) .pinned` style
|
|
522
|
+
STYLE: Add `:host([status="pinned"]) .unpinned` style
|
|
523
|
+
GESTURE: `Gesture.CLICK` exists -> FAIL/PASS
|
|
524
|
+
COMPOSITION: `pin.elements.icon` is a cached reference to the `div` with class `icon` -> FAIL/PASS
|
|
525
|
+
GESTURE: `Gesture.CLICK` on `div` with class `icon` toggle `pin.status` from `Status.UNPINNED` to `Status.PINNED` and visa versa -> FAIL/PASS
|
|
526
|
+
DOCUMENTATION: Generate API Docs
|
|
527
|
+
FEATURE: Pin and Unpin
|
|
528
|
+
```
|
|
529
|
+
|
|
530
|
+
How the policy is applied:
|
|
531
|
+
|
|
532
|
+
- Only the remaining `COMPOSITION` part (`svg.pinned`) is added at the start — just the part this feature requires
|
|
533
|
+
- `STATE` enum values tested in source-declaration order: `Status.PINNED` before `Status.UNPINNED`
|
|
534
|
+
- `STATE` omits null normalization because `status` does not normalize `null`
|
|
535
|
+
- `STATE` includes the attribute-default pair (`status attribute exists`, `status attribute is Status.UNPINNED`) because `status` is compulsory and always written to the DOM
|
|
536
|
+
- `OPERATION` metadata for `PIN` and `UNPIN` batched together before their methods; `TOGGLE` introduced as a separate semantic group with its own metadata-then-method-then-behavior sequence after the paired operations
|
|
537
|
+
- Each event has three behavioral commits: setter existence, handler trigger, payload contract
|
|
538
|
+
- State-driven `STYLE` for pinned/unpinned visual states split per selector — complementary states separated
|
|
539
|
+
- `GESTURE` follows `STYLE`
|
|
540
|
+
- `COMPOSITION` cached reference inserted just-in-time between `GESTURE` metadata and `GESTURE` behavior
|
|
541
|
+
- Gesture behavior commit describes the target by selector (`\`div\` with class \`icon\``)
|
|
542
|
+
- `DOCUMENTATION` → `FEATURE` close the cycle
|
|
543
|
+
|
|
544
|
+
## Feature: Hover Responsiveness
|
|
545
|
+
|
|
546
|
+
Branch: `feature/hover-responsiveness`
|
|
547
|
+
|
|
548
|
+
CSS-only feature — collapsed cycle:
|
|
549
|
+
|
|
550
|
+
```
|
|
551
|
+
STYLE: Add `@media (hover: hover) .icon:hover svg` style
|
|
552
|
+
STYLE: Add `@media (hover: hover) :host([status="unpinned"]) .icon:hover` style
|
|
553
|
+
STYLE: Add `@media (hover: hover) :host([status="pinned"]) .icon:hover` style
|
|
554
|
+
FEATURE: Hover Responsiveness
|
|
555
|
+
```
|
|
556
|
+
|
|
557
|
+
## Feature: Theme Compatibility
|
|
558
|
+
|
|
559
|
+
Branch: `feature/theme-compatibility`
|
|
560
|
+
|
|
561
|
+
CSS-only feature — collapsed cycle:
|
|
562
|
+
|
|
563
|
+
```
|
|
564
|
+
STYLE: Add `@media (prefers-color-scheme: dark) :host` style
|
|
565
|
+
FEATURE: Theme Compatibility
|
|
566
|
+
```
|
|
567
|
+
|
|
568
|
+
---
|
|
569
|
+
|
|
570
|
+
# Policy Checklist
|
|
571
|
+
|
|
572
|
+
A compliant feature branch must:
|
|
573
|
+
|
|
574
|
+
1. Be developed on an isolated feature branch created from main.
|
|
575
|
+
2. Begin every commit message with a canonical prefix from the defined vocabulary (with the sole exceptions of `Initial commit` and version-tag commits).
|
|
576
|
+
3. Use exactly one prefix per commit.
|
|
577
|
+
4. Introduce every behavioral change as a FAIL/PASS commit pair.
|
|
578
|
+
5. Place the FAIL commit immediately before its PASS commit with no commits between them.
|
|
579
|
+
6. Follow the policy-ordered development sequence: `COMPOSITION → STATE → VALIDATION → OPERATION → EVENT → STYLE → GESTURE → DOCUMENTATION → FEATURE`.
|
|
580
|
+
7. Test `COMPOSITION` parts in outside-in structural order using `contains` in the description.
|
|
581
|
+
8. Introduce only the `COMPOSITION` parts required by the current feature.
|
|
582
|
+
9. Follow the fixed coverage sequence for each `STATE` property: metadata → type → enum values (source-declaration order) → getter → default → [attribute default] → setter → mutation → [null normalization] → state→attribute sync → attribute→state sync.
|
|
583
|
+
10. Include attribute-default `STATE` tests only when the attribute is compulsory.
|
|
584
|
+
11. Include null-normalization `STATE` tests only when the setter contains normalization logic.
|
|
585
|
+
12. Use the verb `updates state` and `updates attribute` for mutation and sync commits — not `persists` or `sets`.
|
|
586
|
+
13. Place `VALIDATION` commits after the `STATE` body and before `OPERATION`. Include three pairs: method existence, method error, and setter-level boundary — all under the `VALIDATION:` prefix.
|
|
587
|
+
14. Batch `OPERATION` metadata constants per semantic group, before any method or behavior commits in that group. Composite operations such as `toggle` form their own semantic group.
|
|
588
|
+
15. Use `Invoke` as the uniform verb for all `OPERATION` behavior commits.
|
|
589
|
+
16. Include a precondition in `OPERATION` behavior commits only when the operation requires a specific prior state.
|
|
590
|
+
17. Use `from X to Y and visa versa` for bidirectional `OPERATION` and `GESTURE` behavior commits.
|
|
591
|
+
18. Batch `EVENT` metadata constants per semantic group, before any setter, handler, or payload commits in that group.
|
|
592
|
+
19. Use three commits per event: setter existence, handler trigger (`listener is called when ... changes from X to Y`), and payload (`When ... listener is called then it is called with ...`).
|
|
593
|
+
20. Place state-driven `STYLE` commits after `EVENT` and before `GESTURE`.
|
|
594
|
+
21. Place base element `STYLE` commits just-in-time after the `COMPOSITION` parts they apply to are established.
|
|
595
|
+
22. Use `Define` for base element styles and `Add` for state-driven, gesture-driven, media-query, and extension styles.
|
|
596
|
+
23. Commit one selector per `STYLE` commit. Separate complementary visual states into distinct `STYLE` commits.
|
|
597
|
+
24. Place the `COMPOSITION` cached reference commit between `GESTURE` metadata and `GESTURE` behavior.
|
|
598
|
+
25. Describe `GESTURE` behavior targets by selector phrasing (`\`div\` with class \`icon\``), not by cached reference name.
|
|
599
|
+
26. Close every behavioral feature with `DOCUMENTATION → FEATURE`.
|
|
600
|
+
27. Follow the collapsed cycle (`STYLE → FEATURE`) for CSS-only features.
|
|
601
|
+
28. Use the `FEATURE` commit only as a boundary marker — no new behavior, tests, or styles.
|
|
602
|
+
29. Add `CONFIG` and `COMPOSITION` parts just-in-time at the earliest point they are needed.
|
|
603
|
+
30. Wrap all code references in backticks in commit message descriptions.
|
|
604
|
+
31. Commit test naming convention corrections as standalone `TESTING: Update naming convention` commits, never bundled with behavioral changes.
|
|
605
|
+
32. Use the domain prefix for refactor commits with `refactor` in the message body; do not use a FAIL/PASS pair.
|
|
606
|
+
33. Use `SPECIFICATIONS` for component specification commits.
|
|
607
|
+
34. Use `TYPES` for TypeScript type changes independent of runtime behavior.
|
|
608
|
+
35. Verify every commit message before pushing — prefix present, code references backticked, state values and event names correct, no typos in the feature name.
|