eleven / messenger Public

Clone
git clone https://amadan.net/eleven/messenger

Plain git — no account needed to clone.

Download

Download this file

1{
2 "description": "To-do app event vectors: ensures todo fold handlers produce consistent state. Source of truth: web/static/apps/todo.js",
3 "app": "todo",
4 "source": "web/static/apps/todo.js",
5 "state_shape": { "items": [{ "id": "string", "text": "string", "done": "boolean", "doneAt": "number (epoch ms)", "by": "string (sender name)" }] },
6 "vectors": [
7 {
8 "name": "todo_add",
9 "description": "Add a to-do item (checked off = false by default).",
10 "event": {
11 "type": "todo.add",
12 "id": "todo-1",
13 "text": "buy milk",
14 "name": "Alice"
15 },
16 "expected_state_update": {
17 "items": [
18 {
19 "id": "todo-1",
20 "text": "buy milk",
21 "done": false,
22 "doneAt": 0,
23 "by": "Alice"
24 }
25 ]
26 },
27 "expected_chat_projection": {
28 "text": "added “buy milk”",
29 "action": true
30 },
31 "expected_group_chat_projection": {
32 "text": "added “buy milk” to the to-dos",
33 "action": true
34 }
35 },
36 {
37 "name": "todo_done",
38 "description": "Check off a to-do (mark as done with a timestamp for the grace period animation).",
39 "event": {
40 "type": "todo.done",
41 "id": "todo-1",
42 "ts": 1700000000000
43 },
44 "initial_state": {
45 "items": [
46 {
47 "id": "todo-1",
48 "text": "buy milk",
49 "done": false,
50 "doneAt": 0,
51 "by": "Alice"
52 }
53 ]
54 },
55 "expected_state_update": {
56 "items": [
57 {
58 "id": "todo-1",
59 "text": "buy milk",
60 "done": true,
61 "doneAt": 1700000000000,
62 "by": "Alice"
63 }
64 ]
65 },
66 "note": "doneAt drives the grace-period animation (the check fills immediately, the row stays visible ~750ms, then tucks into Completed — the Reminders-style completion flourish, no countdown ring). No chat projection for done/reopen/remove (only add shows as an action line)."
67 },
68 {
69 "name": "todo_reopen",
70 "description": "Uncheck a to-do (mark as not done, clear the doneAt timestamp).",
71 "event": {
72 "type": "todo.reopen",
73 "id": "todo-1"
74 },
75 "initial_state": {
76 "items": [
77 {
78 "id": "todo-1",
79 "text": "buy milk",
80 "done": true,
81 "doneAt": 1700000000000,
82 "by": "Alice"
83 }
84 ]
85 },
86 "expected_state_update": {
87 "items": [
88 {
89 "id": "todo-1",
90 "text": "buy milk",
91 "done": false,
92 "doneAt": 0,
93 "by": "Alice"
94 }
95 ]
96 }
97 },
98 {
99 "name": "todo_remove",
100 "description": "Delete a to-do item permanently.",
101 "event": {
102 "type": "todo.remove",
103 "id": "todo-1"
104 },
105 "initial_state": {
106 "items": [
107 {
108 "id": "todo-1",
109 "text": "buy milk",
110 "done": false,
111 "doneAt": 0,
112 "by": "Alice"
113 },
114 {
115 "id": "todo-2",
116 "text": "call mum",
117 "done": false,
118 "doneAt": 0,
119 "by": "Alice"
120 }
121 ]
122 },
123 "expected_state_update": {
124 "items": [
125 {
126 "id": "todo-2",
127 "text": "call mum",
128 "done": false,
129 "doneAt": 0,
130 "by": "Alice"
131 }
132 ]
133 }
134 },
135 {
136 "name": "todo_move_after",
137 "description": "Reorder: move an item to immediately follow `after` in the array (drag-to-reorder).",
138 "event": {
139 "type": "todo.move",
140 "id": "todo-1",
141 "after": "todo-2"
142 },
143 "initial_state": {
144 "items": [
145 { "id": "todo-1", "text": "buy milk", "done": false, "doneAt": 0, "by": "Alice" },
146 { "id": "todo-2", "text": "call mum", "done": false, "doneAt": 0, "by": "Alice" },
147 { "id": "todo-3", "text": "water plants", "done": false, "doneAt": 0, "by": "Alice" }
148 ]
149 },
150 "expected_state_update": {
151 "items": [
152 { "id": "todo-2", "text": "call mum", "done": false, "doneAt": 0, "by": "Alice" },
153 { "id": "todo-1", "text": "buy milk", "done": false, "doneAt": 0, "by": "Alice" },
154 { "id": "todo-3", "text": "water plants", "done": false, "doneAt": 0, "by": "Alice" }
155 ]
156 },
157 "note": "No chat projection — reorders are silent, like done/remove. The list renders newest-first (_index desc), so 'after' anchors in ARRAY order: following todo-2 in the array displays ABOVE it."
158 },
159 {
160 "name": "todo_move_to_start",
161 "description": "Reorder: an empty `after` moves the item to the array's start (the visual bottom of a newest-first list).",
162 "event": {
163 "type": "todo.move",
164 "id": "todo-3",
165 "after": ""
166 },
167 "initial_state": {
168 "items": [
169 { "id": "todo-1", "text": "buy milk", "done": false, "doneAt": 0, "by": "Alice" },
170 { "id": "todo-2", "text": "call mum", "done": false, "doneAt": 0, "by": "Alice" },
171 { "id": "todo-3", "text": "water plants", "done": false, "doneAt": 0, "by": "Alice" }
172 ]
173 },
174 "expected_state_update": {
175 "items": [
176 { "id": "todo-3", "text": "water plants", "done": false, "doneAt": 0, "by": "Alice" },
177 { "id": "todo-1", "text": "buy milk", "done": false, "doneAt": 0, "by": "Alice" },
178 { "id": "todo-2", "text": "call mum", "done": false, "doneAt": 0, "by": "Alice" }
179 ]
180 }
181 },
182 {
183 "name": "todo_move_unknown_anchor",
184 "description": "Reorder resilience: moving after an id that no longer exists (removed concurrently) is a no-op.",
185 "event": {
186 "type": "todo.move",
187 "id": "todo-1",
188 "after": "todo-gone"
189 },
190 "initial_state": {
191 "items": [
192 { "id": "todo-1", "text": "buy milk", "done": false, "doneAt": 0, "by": "Alice" },
193 { "id": "todo-2", "text": "call mum", "done": false, "doneAt": 0, "by": "Alice" }
194 ]
195 },
196 "expected_state_update": {
197 "items": [
198 { "id": "todo-1", "text": "buy milk", "done": false, "doneAt": 0, "by": "Alice" },
199 { "id": "todo-2", "text": "call mum", "done": false, "doneAt": 0, "by": "Alice" }
200 ]
201 }
202 },
203 {
204 "name": "todo_grace_period_partition",
205 "description": "Grace period: active (not done or recently checked within 750ms) vs. archived (done + >750ms ago).",
206 "note": "This is derived in render from Date.now() and doneAt, never by a timer mutating state. Native should implement the same partitioning logic: a done item with doneAt > (now - 750) stays in 'active', others move to 'Completed' section.",
207 "constants": {
208 "TODO_GRACE_MS": 750
209 }
210 }
211 ]
212}
213