openclaw-linear 0.1.0 → 0.2.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/README.md CHANGED
@@ -6,7 +6,9 @@ Linear webhook integration for OpenClaw. Receives Linear events, filters and rou
6
6
 
7
7
  - **Webhook handler** — receives Linear webhook events with HMAC signature verification (timing-safe), duplicate delivery detection, and body size limits
8
8
  - **Event router** — filters by team and event type, routes issue assignments and comment mentions to the configured agent
9
- - **Debounced dispatch** — batches events within a configurable window into a single consolidated message so the agent can triage before acting
9
+ - **Debounced dispatch** — batches events within a configurable window before dispatching
10
+ - **Work queue** — deterministic queue intake writes structured items to `queue/work-queue.json` with priority sorting, deduplication, and 24h auto-cleanup of completed items — no LLM tokens spent on triage
11
+ - **Crash recovery** — resets stale `in_progress` queue items to `pending` on gateway startup
10
12
 
11
13
  ## Install
12
14
 
@@ -75,22 +77,50 @@ Linear webhook POST
75
77
  → Duplicate delivery check (10-min TTL, 10k cap)
76
78
  → Event router filters by team/type, matches user via agentMapping
77
79
  → wake actions enqueued into debouncer (keyed by agent ID)
78
- → After debounce window expires, consolidated message dispatched to agent
80
+ → After debounce window expires:
81
+ → Notifications parsed and written to queue/work-queue.json (deterministic, no LLM)
82
+ → Deduped against existing non-done items — skips agent dispatch if nothing new
83
+ → Agent receives minimal message: "3 new Linear notification(s) queued."
79
84
  ```
80
85
 
81
- When multiple events arrive within the debounce window, the agent receives a single numbered message:
82
-
86
+ ## Work Queue
87
+
88
+ The plugin writes structured items to `queue/work-queue.json`:
89
+
90
+ ```json
91
+ {
92
+ "items": [
93
+ {
94
+ "id": "ENG-42",
95
+ "issueId": "ENG-42",
96
+ "event": "issue.assigned",
97
+ "summary": "Fix login bug",
98
+ "status": "pending",
99
+ "priority": 1,
100
+ "addedAt": "2026-02-14T15:45:00.000Z",
101
+ "startedAt": null,
102
+ "completedAt": null
103
+ }
104
+ ]
105
+ }
83
106
  ```
84
- You have 3 new Linear notifications:
85
107
 
86
- 1. [Assigned] ENG-42: Fix login bug
87
- 2. [Assigned] ENG-43: Update API docs
88
- 3. [Mentioned] ENG-40: Auth flow: "Can you review this?"
108
+ ### Priority
89
109
 
90
- Review and prioritize before starting work.
91
- ```
110
+ | Priority | Event |
111
+ |----------|-------|
112
+ | 1 | `issue.assigned` |
113
+ | 2 | `issue.reassigned` |
114
+ | 3 | `comment.mention` |
115
+ | 4 | `issue.unassigned` |
116
+
117
+ ### Lifecycle
118
+
119
+ Items progress through statuses: `pending` → `in_progress` → `done`.
92
120
 
93
- Single events are passed through as-is (no numbered wrapper).
121
+ - **Deduplication** keyed by `issueId + event`, only checked against non-done items. Completed items can be re-queued (e.g. re-assignment after completion).
122
+ - **Cleanup** — done items older than 24 hours are purged automatically during intake.
123
+ - **Recovery** — on gateway startup, any `in_progress` items are reset to `pending` to recover from crashes.
94
124
 
95
125
  ## Development
96
126
 
@@ -1 +1 @@
1
- {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,iBAAiB,EAAE,MAAM,qBAAqB,CAAC;AAE7D,OAAO,EAAqB,KAAK,YAAY,EAAE,MAAM,mBAAmB,CAAC;AAYzE,wBAAgB,yBAAyB,CAAC,OAAO,EAAE,YAAY,EAAE,GAAG,MAAM,CAYzE;AA0ED,wBAAgB,QAAQ,CAAC,GAAG,EAAE,iBAAiB,GAAG,IAAI,CAyErD;AAED,wBAAsB,UAAU,CAAC,GAAG,EAAE,iBAAiB,GAAG,OAAO,CAAC,IAAI,CAAC,CAStE;AAED,QAAA,MAAM,MAAM;;;;;;CAYX,CAAC;AAEF,eAAe,MAAM,CAAC"}
1
+ {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,iBAAiB,EAAE,MAAM,qBAAqB,CAAC;AAE7D,OAAO,EAAqB,KAAK,YAAY,EAAE,MAAM,mBAAmB,CAAC;AAazE,wBAAgB,yBAAyB,CAAC,OAAO,EAAE,YAAY,EAAE,GAAG,MAAM,CAYzE;AAqFD,wBAAgB,QAAQ,CAAC,GAAG,EAAE,iBAAiB,GAAG,IAAI,CAmFrD;AAED,wBAAsB,UAAU,CAAC,GAAG,EAAE,iBAAiB,GAAG,OAAO,CAAC,IAAI,CAAC,CAStE;AAED,QAAA,MAAM,MAAM;;;;;;CAYX,CAAC;AAEF,eAAe,MAAM,CAAC"}
package/dist/index.js CHANGED
@@ -1,5 +1,6 @@
1
1
  import { createWebhookHandler } from "./webhook-handler.js";
2
2
  import { createEventRouter } from "./event-router.js";
3
+ import { handleIntake, handleRecovery } from "./work-queue.js";
3
4
  const CHANNEL_ID = "linear";
4
5
  const DEFAULT_DEBOUNCE_MS = 30_000;
5
6
  const EVENT_LABELS = {
@@ -29,7 +30,7 @@ function formatActionSummary(action) {
29
30
  }
30
31
  return action.issueLabel || action.detail;
31
32
  }
32
- async function dispatchConsolidatedActions(actions, api) {
33
+ async function dispatchConsolidatedActions(actions, api, queuePath) {
33
34
  if (actions.length === 0)
34
35
  return;
35
36
  const core = api.runtime;
@@ -44,7 +45,15 @@ async function dispatchConsolidatedActions(actions, api) {
44
45
  id: first.linearUserId,
45
46
  },
46
47
  });
47
- const body = formatConsolidatedMessage(actions);
48
+ // Write to queue deterministically — no LLM involved
49
+ const rawBody = formatConsolidatedMessage(actions);
50
+ const added = handleIntake(rawBody, queuePath);
51
+ if (added === 0) {
52
+ api.logger.info("[linear] All notifications deduped — skipping agent dispatch");
53
+ return;
54
+ }
55
+ // Agent gets a minimal notification pointing to the queue
56
+ const body = `${added} new Linear notification(s) queued. Pending items in queue/work-queue.json.`;
48
57
  const ctx = core.channel.reply.finalizeInboundContext({
49
58
  Body: body,
50
59
  BodyForAgent: body,
@@ -94,6 +103,14 @@ export function activate(api) {
94
103
  const debounceMs = (typeof rawDebounceMs === "number" && rawDebounceMs > 0)
95
104
  ? rawDebounceMs
96
105
  : DEFAULT_DEBOUNCE_MS;
106
+ const queuePath = api.resolvePath("queue/work-queue.json");
107
+ // Reset stale in_progress items on gateway startup
108
+ api.on("gateway_start", () => {
109
+ const recovered = handleRecovery(queuePath);
110
+ if (recovered > 0) {
111
+ api.logger.info(`[linear] Recovered ${recovered} stale queue item(s)`);
112
+ }
113
+ });
97
114
  const route = createEventRouter({
98
115
  agentMapping,
99
116
  logger: api.logger,
@@ -105,7 +122,7 @@ export function activate(api) {
105
122
  buildKey: (action) => action.agentId,
106
123
  shouldDebounce: () => true,
107
124
  onFlush: async (actions) => {
108
- await dispatchConsolidatedActions(actions, api);
125
+ await dispatchConsolidatedActions(actions, api, queuePath);
109
126
  },
110
127
  onError: (err) => {
111
128
  api.logger.error(`[linear] Debounce flush failed: ${err instanceof Error ? err.message : String(err)}`);
package/dist/index.js.map CHANGED
@@ -1 +1 @@
1
- {"version":3,"file":"index.js","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"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"}
1
+ {"version":3,"file":"index.js","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,38 @@
1
+ export interface QueueItem {
2
+ id: string;
3
+ issueId: string;
4
+ event: string;
5
+ summary: string;
6
+ status: "pending" | "in_progress" | "done";
7
+ priority: number;
8
+ addedAt: string;
9
+ startedAt: string | null;
10
+ completedAt: string | null;
11
+ }
12
+ export interface WorkQueue {
13
+ items: QueueItem[];
14
+ }
15
+ export declare function parseNotificationMessage(message: string): Array<{
16
+ id: string;
17
+ event: string;
18
+ summary: string;
19
+ }>;
20
+ export declare function readQueue(queuePath: string): WorkQueue;
21
+ export declare function writeQueue(queuePath: string, queue: WorkQueue): void;
22
+ /**
23
+ * Parse a consolidated notification message and append new items to the work queue.
24
+ * Deduplicates against non-done items only (completed items can be re-queued).
25
+ * Returns the number of new items added.
26
+ */
27
+ export declare function handleIntake(message: string, queuePath: string): number;
28
+ /**
29
+ * Reset stale in_progress items to pending.
30
+ * Called on gateway startup to recover from crashes/restarts.
31
+ */
32
+ export declare function handleRecovery(queuePath: string): number;
33
+ /**
34
+ * Remove done items older than maxAgeMs from the queue.
35
+ * Mutates the queue in place. Returns number of items purged.
36
+ */
37
+ export declare function cleanupDoneItems(queue: WorkQueue, maxAgeMs?: number): number;
38
+ //# sourceMappingURL=work-queue.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"work-queue.d.ts","sourceRoot":"","sources":["../src/work-queue.ts"],"names":[],"mappings":"AAaA,MAAM,WAAW,SAAS;IACxB,EAAE,EAAE,MAAM,CAAC;IACX,OAAO,EAAE,MAAM,CAAC;IAChB,KAAK,EAAE,MAAM,CAAC;IACd,OAAO,EAAE,MAAM,CAAC;IAChB,MAAM,EAAE,SAAS,GAAG,aAAa,GAAG,MAAM,CAAC;IAC3C,QAAQ,EAAE,MAAM,CAAC;IACjB,OAAO,EAAE,MAAM,CAAC;IAChB,SAAS,EAAE,MAAM,GAAG,IAAI,CAAC;IACzB,WAAW,EAAE,MAAM,GAAG,IAAI,CAAC;CAC5B;AAED,MAAM,WAAW,SAAS;IACxB,KAAK,EAAE,SAAS,EAAE,CAAC;CACpB;AAgGD,wBAAgB,wBAAwB,CACtC,OAAO,EAAE,MAAM,GACd,KAAK,CAAC;IAAE,EAAE,EAAE,MAAM,CAAC;IAAC,KAAK,EAAE,MAAM,CAAC;IAAC,OAAO,EAAE,MAAM,CAAA;CAAE,CAAC,CAcvD;AAID,wBAAgB,SAAS,CAAC,SAAS,EAAE,MAAM,GAAG,SAAS,CAOtD;AAED,wBAAgB,UAAU,CAAC,SAAS,EAAE,MAAM,EAAE,KAAK,EAAE,SAAS,GAAG,IAAI,CAuBpE;AAID;;;;GAIG;AACH,wBAAgB,YAAY,CAAC,OAAO,EAAE,MAAM,EAAE,SAAS,EAAE,MAAM,GAAG,MAAM,CA0CvE;AAID;;;GAGG;AACH,wBAAgB,cAAc,CAAC,SAAS,EAAE,MAAM,GAAG,MAAM,CA0BxD;AAMD;;;GAGG;AACH,wBAAgB,gBAAgB,CAC9B,KAAK,EAAE,SAAS,EAChB,QAAQ,GAAE,MAAwB,GACjC,MAAM,CAWR"}
@@ -0,0 +1,218 @@
1
+ import { readFileSync, existsSync, mkdirSync, openSync, writeSync, fsyncSync, closeSync, unlinkSync, renameSync, } from "node:fs";
2
+ import { dirname } from "node:path";
3
+ const EVENT_PRIORITY = {
4
+ "issue.assigned": 1,
5
+ "issue.reassigned": 2,
6
+ "comment.mention": 3,
7
+ "issue.unassigned": 4,
8
+ };
9
+ // --- Parsing ---
10
+ function parseNotificationLine(line) {
11
+ // Multi-notification with quoted comment: "N. [Mentioned] TEAM-123: "comment text""
12
+ const mentionMatch = line.match(/^\d+\.\s+\[Mentioned\]\s+([A-Z]+-\d+):\s*"(.+)"$/);
13
+ if (mentionMatch) {
14
+ return {
15
+ id: mentionMatch[1],
16
+ event: "comment.mention",
17
+ summary: mentionMatch[2].trim(),
18
+ };
19
+ }
20
+ // Multi-notification format: "N. [Label] TEAM-123: summary"
21
+ const multiMatch = line.match(/^\d+\.\s+\[(\w+(?:\s+\w+)?)\]\s+([A-Z]+-\d+):\s*(.+)$/);
22
+ if (multiMatch) {
23
+ const [, label, id, summary] = multiMatch;
24
+ return { id, event: labelToEvent(label), summary: summary.trim() };
25
+ }
26
+ // Single: "Assigned to issue TEAM-123: summary"
27
+ const assignedMatch = line.match(/^Assigned to issue ([A-Z]+-\d+):\s*(.+)$/);
28
+ if (assignedMatch) {
29
+ return {
30
+ id: assignedMatch[1],
31
+ event: "issue.assigned",
32
+ summary: assignedMatch[2].trim(),
33
+ };
34
+ }
35
+ // Single: "Unassigned from issue TEAM-123: summary"
36
+ const unassignedMatch = line.match(/^Unassigned from issue ([A-Z]+-\d+):\s*(.+)$/);
37
+ if (unassignedMatch) {
38
+ return {
39
+ id: unassignedMatch[1],
40
+ event: "issue.unassigned",
41
+ summary: unassignedMatch[2].trim(),
42
+ };
43
+ }
44
+ // Single: "Reassigned away from issue TEAM-123: summary"
45
+ const reassignedMatch = line.match(/^Reassigned away from issue ([A-Z]+-\d+):\s*(.+)$/);
46
+ if (reassignedMatch) {
47
+ return {
48
+ id: reassignedMatch[1],
49
+ event: "issue.reassigned",
50
+ summary: reassignedMatch[2].trim(),
51
+ };
52
+ }
53
+ // Single: "Mentioned in comment on issue TEAM-123: summary\n\n> body"
54
+ const mentionedMatch = line.match(/^Mentioned in comment on issue ([A-Z]+-\d+):\s*(.+?)(?:\n|$)/);
55
+ if (mentionedMatch) {
56
+ return {
57
+ id: mentionedMatch[1],
58
+ event: "comment.mention",
59
+ summary: mentionedMatch[2].trim(),
60
+ };
61
+ }
62
+ return null;
63
+ }
64
+ function labelToEvent(label) {
65
+ const map = {
66
+ Assigned: "issue.assigned",
67
+ Unassigned: "issue.unassigned",
68
+ Reassigned: "issue.reassigned",
69
+ Mentioned: "comment.mention",
70
+ };
71
+ return map[label] ?? `unknown.${label.toLowerCase()}`;
72
+ }
73
+ export function parseNotificationMessage(message) {
74
+ const results = [];
75
+ if (message.startsWith("You have ")) {
76
+ for (const line of message.split("\n").filter((l) => l.trim())) {
77
+ const parsed = parseNotificationLine(line.trim());
78
+ if (parsed)
79
+ results.push(parsed);
80
+ }
81
+ }
82
+ else {
83
+ const parsed = parseNotificationLine(message.trim());
84
+ if (parsed)
85
+ results.push(parsed);
86
+ }
87
+ return results;
88
+ }
89
+ // --- File I/O ---
90
+ export function readQueue(queuePath) {
91
+ if (!existsSync(queuePath))
92
+ return { items: [] };
93
+ try {
94
+ return JSON.parse(readFileSync(queuePath, "utf-8"));
95
+ }
96
+ catch {
97
+ return { items: [] };
98
+ }
99
+ }
100
+ export function writeQueue(queuePath, queue) {
101
+ const dir = dirname(queuePath);
102
+ if (!existsSync(dir))
103
+ mkdirSync(dir, { recursive: true });
104
+ const tmpPath = `${queuePath}.tmp.${process.pid}.${Date.now()}`;
105
+ const content = JSON.stringify(queue, null, 2) + "\n";
106
+ try {
107
+ const fd = openSync(tmpPath, "w");
108
+ try {
109
+ writeSync(fd, content, 0, "utf-8");
110
+ fsyncSync(fd);
111
+ }
112
+ finally {
113
+ closeSync(fd);
114
+ }
115
+ renameSync(tmpPath, queuePath);
116
+ }
117
+ catch (err) {
118
+ try {
119
+ unlinkSync(tmpPath);
120
+ }
121
+ catch {
122
+ /* ignore cleanup errors */
123
+ }
124
+ throw err;
125
+ }
126
+ }
127
+ // --- Intake ---
128
+ /**
129
+ * Parse a consolidated notification message and append new items to the work queue.
130
+ * Deduplicates against non-done items only (completed items can be re-queued).
131
+ * Returns the number of new items added.
132
+ */
133
+ export function handleIntake(message, queuePath) {
134
+ const parsed = parseNotificationMessage(message);
135
+ if (parsed.length === 0)
136
+ return 0;
137
+ const queue = readQueue(queuePath);
138
+ // Only dedup against non-done items so re-assignments after completion work
139
+ const existingKeys = new Set(queue.items
140
+ .filter((item) => item.status !== "done")
141
+ .map((item) => `${item.issueId ?? item.id}:${item.event}`));
142
+ let added = 0;
143
+ const now = new Date().toISOString();
144
+ for (const entry of parsed) {
145
+ const dedupKey = `${entry.id}:${entry.event}`;
146
+ if (existingKeys.has(dedupKey))
147
+ continue;
148
+ queue.items.push({
149
+ id: entry.id,
150
+ issueId: entry.id,
151
+ event: entry.event,
152
+ summary: entry.summary,
153
+ status: "pending",
154
+ priority: EVENT_PRIORITY[entry.event] ?? 5,
155
+ addedAt: now,
156
+ startedAt: null,
157
+ completedAt: null,
158
+ });
159
+ existingKeys.add(dedupKey);
160
+ added++;
161
+ }
162
+ if (added > 0) {
163
+ cleanupDoneItems(queue);
164
+ queue.items.sort((a, b) => a.priority - b.priority);
165
+ writeQueue(queuePath, queue);
166
+ }
167
+ return added;
168
+ }
169
+ // --- Recovery ---
170
+ /**
171
+ * Reset stale in_progress items to pending.
172
+ * Called on gateway startup to recover from crashes/restarts.
173
+ */
174
+ export function handleRecovery(queuePath) {
175
+ if (!existsSync(queuePath))
176
+ return 0;
177
+ let queue;
178
+ try {
179
+ queue = JSON.parse(readFileSync(queuePath, "utf-8"));
180
+ }
181
+ catch {
182
+ return 0;
183
+ }
184
+ if (!Array.isArray(queue.items) || queue.items.length === 0)
185
+ return 0;
186
+ let recovered = 0;
187
+ for (const item of queue.items) {
188
+ if (item.status === "in_progress") {
189
+ item.status = "pending";
190
+ item.startedAt = null;
191
+ recovered++;
192
+ }
193
+ }
194
+ if (recovered > 0) {
195
+ writeQueue(queuePath, queue);
196
+ }
197
+ return recovered;
198
+ }
199
+ // --- Cleanup ---
200
+ const DONE_MAX_AGE_MS = 24 * 60 * 60 * 1000; // 24 hours
201
+ /**
202
+ * Remove done items older than maxAgeMs from the queue.
203
+ * Mutates the queue in place. Returns number of items purged.
204
+ */
205
+ export function cleanupDoneItems(queue, maxAgeMs = DONE_MAX_AGE_MS) {
206
+ const cutoff = Date.now() - maxAgeMs;
207
+ const before = queue.items.length;
208
+ queue.items = queue.items.filter((item) => {
209
+ if (item.status !== "done")
210
+ return true;
211
+ const completedAt = item.completedAt
212
+ ? new Date(item.completedAt).getTime()
213
+ : 0;
214
+ return completedAt > cutoff;
215
+ });
216
+ return before - queue.items.length;
217
+ }
218
+ //# sourceMappingURL=work-queue.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"work-queue.js","sourceRoot":"","sources":["../src/work-queue.ts"],"names":[],"mappings":"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"}
@@ -2,6 +2,7 @@
2
2
  "id": "linear",
3
3
  "name": "Linear",
4
4
  "description": "Linear project management integration for OpenClaw",
5
+ "skills": ["./skills"],
5
6
  "configSchema": {
6
7
  "type": "object",
7
8
  "additionalProperties": false,
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "openclaw-linear",
3
- "version": "0.1.0",
3
+ "version": "0.2.0",
4
4
  "description": "Linear webhook integration for OpenClaw — receives events, routes them, and dispatches consolidated notifications to agents",
5
5
  "type": "module",
6
6
  "main": "dist/index.js",
@@ -13,6 +13,7 @@
13
13
  },
14
14
  "files": [
15
15
  "dist",
16
+ "skills",
16
17
  "openclaw.plugin.json",
17
18
  "LICENSE",
18
19
  "README.md"
@@ -0,0 +1,48 @@
1
+ ---
2
+ name: linear-queue
3
+ description: Work queue processing for Linear notifications. Guides the agent through reading, prioritizing, and completing queued Linear items.
4
+ metadata: { "openclaw": { "always": true } }
5
+ ---
6
+
7
+ # Linear Work Queue
8
+
9
+ You have a work queue at `queue/work-queue.json` containing Linear notifications that need your attention. The queue is the source of truth — don't parse raw notification messages yourself.
10
+
11
+ ## Queue item structure
12
+
13
+ ```json
14
+ {
15
+ "id": "TEAM-123",
16
+ "issueId": "TEAM-123",
17
+ "event": "issue.assigned",
18
+ "summary": "Issue title or comment text",
19
+ "status": "pending",
20
+ "priority": 1,
21
+ "addedAt": "ISO timestamp",
22
+ "startedAt": null,
23
+ "completedAt": null
24
+ }
25
+ ```
26
+
27
+ ## Event types and expected actions
28
+
29
+ | Event | Priority | Action |
30
+ |---|---|---|
31
+ | `issue.assigned` | 1 | You've been assigned an issue. Read the issue details with the Linear tools, understand the requirements, and begin work. |
32
+ | `issue.reassigned` | 2 | An issue was reassigned away from you. Acknowledge the change, stop any related work, and add a handoff comment if you have useful context. |
33
+ | `comment.mention` | 3 | You were mentioned in a comment. Read the comment, understand what's being asked, and respond. |
34
+ | `issue.unassigned` | 4 | You were unassigned from an issue. Acknowledge and stop any related work. |
35
+
36
+ ## Processing workflow
37
+
38
+ 1. **Read** the queue file (`queue/work-queue.json`).
39
+ 2. **Skip** if there are no `pending` items.
40
+ 3. **Pick** the highest-priority `pending` item (lowest priority number first; break ties by `addedAt`, oldest first).
41
+ 4. **Mark `in_progress`**: set `status` to `"in_progress"` and `startedAt` to the current ISO timestamp. Write the queue back to disk.
42
+ 5. **Act**: use the Linear tools (list-issues, update-issue, add-comment) to handle the item per the event type table above.
43
+ 6. **Mark `done`**: set `status` to `"done"` and `completedAt` to the current ISO timestamp. Write the queue back to disk.
44
+ 7. **Repeat** from step 1 until no `pending` items remain.
45
+
46
+ ## File update rules
47
+
48
+ Always do atomic read-modify-write cycles: read the full file, modify the target item in memory, write the entire file back. Never partially update the file.