@luckydraw/cumulus 1.0.39 → 1.0.41

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.
Files changed (39) hide show
  1. package/CHANGELOG.md +20 -0
  2. package/dist/gateway/adapters/webchat.d.ts +3 -0
  3. package/dist/gateway/adapters/webchat.d.ts.map +1 -1
  4. package/dist/gateway/adapters/webchat.js +70 -0
  5. package/dist/gateway/adapters/webchat.js.map +1 -1
  6. package/dist/gateway/config.d.ts +16 -2
  7. package/dist/gateway/config.d.ts.map +1 -1
  8. package/dist/gateway/config.js +12 -6
  9. package/dist/gateway/config.js.map +1 -1
  10. package/dist/gateway/daemon.d.ts +1 -0
  11. package/dist/gateway/daemon.d.ts.map +1 -1
  12. package/dist/gateway/daemon.js +4 -0
  13. package/dist/gateway/daemon.js.map +1 -1
  14. package/dist/gateway/one-shot-model.d.ts +92 -0
  15. package/dist/gateway/one-shot-model.d.ts.map +1 -0
  16. package/dist/gateway/one-shot-model.js +191 -0
  17. package/dist/gateway/one-shot-model.js.map +1 -0
  18. package/dist/gateway/server.d.ts +3 -0
  19. package/dist/gateway/server.d.ts.map +1 -1
  20. package/dist/gateway/server.js +4 -0
  21. package/dist/gateway/server.js.map +1 -1
  22. package/dist/gateway/stall-detection.d.ts +19 -50
  23. package/dist/gateway/stall-detection.d.ts.map +1 -1
  24. package/dist/gateway/stall-detection.js +27 -190
  25. package/dist/gateway/stall-detection.js.map +1 -1
  26. package/dist/gateway/static/widget.js +139 -18
  27. package/dist/gateway/tasks-refresh.d.ts +115 -0
  28. package/dist/gateway/tasks-refresh.d.ts.map +1 -0
  29. package/dist/gateway/tasks-refresh.js +333 -0
  30. package/dist/gateway/tasks-refresh.js.map +1 -0
  31. package/dist/gateway/tasks.d.ts +18 -0
  32. package/dist/gateway/tasks.d.ts.map +1 -1
  33. package/dist/gateway/tasks.js +42 -7
  34. package/dist/gateway/tasks.js.map +1 -1
  35. package/dist/gateway/worker.d.ts +86 -0
  36. package/dist/gateway/worker.d.ts.map +1 -0
  37. package/dist/gateway/worker.js +0 -0
  38. package/dist/gateway/worker.js.map +1 -0
  39. package/package.json +1 -1
@@ -0,0 +1,115 @@
1
+ /**
2
+ * Task 179 — the Refresh button's actual work.
3
+ *
4
+ * Karl's three answers set the shape of this file:
5
+ *
6
+ * 1. *"Write it straight away, but commit to git first so I can undo."* — so a
7
+ * snapshot commit is a PRECONDITION, and a file outside a git repo is
8
+ * refused outright. The permission to write without asking was bought with
9
+ * the undo; without a repo there is no undo, so the permission lapses.
10
+ * 2. *"Both — convert to the new format AND re-check every task's state."* — one
11
+ * model call does both, because they are the same rewrite.
12
+ * 3. *"Build it as a general facility."* — which is `worker.ts`; this module is
13
+ * only its first caller.
14
+ *
15
+ * The load-bearing part is not the prompt. A model handed a whole file and asked
16
+ * for a whole file back will, sooner or later, quietly drop a row — so the answer
17
+ * is checked before it is written, and a run that loses a task id, fails to
18
+ * parse, or comes back without the marker is refused WHOLE. The git snapshot is
19
+ * the belt; these checks are the braces.
20
+ *
21
+ * Task 180, after the first live press: `## Done` is now split off before the
22
+ * prompt is built and re-attached after, and an empty queue is refused outright.
23
+ * See {@link splitOffDone} — the model was rewriting the one section the format
24
+ * says is never sent to a model, because it was the only thing left to touch.
25
+ */
26
+ import { type TasksPayload } from './tasks.js';
27
+ import { type RunWorkerOptions } from './worker.js';
28
+ /** A refusal with a reason the drawer shows verbatim. */
29
+ export declare class TasksRefreshError extends Error {
30
+ }
31
+ /**
32
+ * Every task id the file currently carries.
33
+ *
34
+ * Deliberately permissive, and matched against the RAW text rather than a parse:
35
+ * the un-migrated files this exists to convert do not parse at all, and they are
36
+ * the ones with the most to lose. Both shapes seen in the wild are covered —
37
+ * `- [ ] 179 · title` (schema) and `- [◕] 178: title` (what came before).
38
+ *
39
+ * The `## Format` section is skipped, and that is not a nicety: the manual
40
+ * carried inside every migrated file demonstrates the grammar with EXAMPLE rows
41
+ * (`- [ ] 073 · StorePool reaping`, `- [⊘] 084 · Creative-writing benchmark`).
42
+ * Counting those as tasks would demand the model preserve four ids that do not
43
+ * exist, so every already-migrated file would fail the drop check and Refresh
44
+ * would work only on un-migrated ones. Found by test, not by reading.
45
+ */
46
+ export declare function taskIdsIn(content: string): string[];
47
+ export interface DoneSplit {
48
+ /** Everything above `## Done`. The only part a model ever sees of a migrated file. */
49
+ head: string;
50
+ /** The `## Done` heading and everything under it, or null if the file has no such section. */
51
+ done: string | null;
52
+ }
53
+ /**
54
+ * Split a tasks file at its `## Done` heading.
55
+ *
56
+ * Task 180: `## Done` is the record of work that has left the queue, and the
57
+ * format's own rule is that it is never sent to a model. The first live Refresh
58
+ * proved why that has to be enforced with scissors rather than with a sentence in
59
+ * the prompt: handed the whole file and an empty queue, the model spent its one
60
+ * call appending "the reload has happened" to twelve finished tasks.
61
+ *
62
+ * The LAST heading wins, because the manual carried inside every migrated file
63
+ * quotes `## Done` while describing the grammar.
64
+ */
65
+ export declare function splitOffDone(content: string): DoneSplit;
66
+ /**
67
+ * Re-attach the withheld record to the model's answer.
68
+ *
69
+ * The original section goes back byte-for-byte. If the model emitted a `## Done`
70
+ * of its own — it is told not to — those rows are spliced in ABOVE the preserved
71
+ * ones rather than discarded: dropping them would lose their ids, and the drop
72
+ * check would then refuse an answer whose only sin was disobedience.
73
+ */
74
+ export declare function stitchDone(answer: string, originalDone: string): string;
75
+ /** Strip a ```-fenced wrapper, which a chat-tuned model adds however firmly it is told not to. */
76
+ export declare function unfence(answer: string): string;
77
+ /**
78
+ * Commit the file as it stands, and return whether there was anything to commit.
79
+ *
80
+ * Scoped by pathspec on BOTH commands, so a refresh never sweeps up whatever else
81
+ * the user happened to have staged. "Nothing to commit" is success, not failure:
82
+ * the file is already identical to HEAD, so HEAD *is* the undo point.
83
+ *
84
+ * Not being in a git repo is the one hard refusal here — see the module comment.
85
+ */
86
+ export declare function snapshotBeforeRefresh(filePath: string): boolean;
87
+ export interface RefreshPromptInput {
88
+ content: string;
89
+ migrated: boolean;
90
+ docs: string[];
91
+ version: string;
92
+ /** True when `## Done` was split off and will be re-attached unchanged (task 180). */
93
+ doneWithheld: boolean;
94
+ }
95
+ export declare function buildRefreshPrompt(input: RefreshPromptInput): string;
96
+ export interface RefreshResult {
97
+ payload: TasksPayload;
98
+ /** False when the file was already identical to HEAD — HEAD is then the undo point. */
99
+ committed: boolean;
100
+ /** Ids the model dropped. Non-empty only on the refusal path, for the message. */
101
+ droppedIds: string[];
102
+ }
103
+ export interface RefreshOptions extends RunWorkerOptions {
104
+ /** Test seam: run this instead of the model. Production leaves it unset. */
105
+ runModel?: (prompt: string) => Promise<string>;
106
+ }
107
+ /**
108
+ * Convert and re-check a thread's tasks file in place.
109
+ *
110
+ * Order matters and is the design: resolve → snapshot → ask → CHECK → write.
111
+ * Nothing between the snapshot and the write touches disk, so every refusal below
112
+ * leaves the file byte-identical to what was just committed.
113
+ */
114
+ export declare function refreshTasksFile(threadName: string, opts?: RefreshOptions): Promise<RefreshResult>;
115
+ //# sourceMappingURL=tasks-refresh.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"tasks-refresh.d.ts","sourceRoot":"","sources":["../../src/gateway/tasks-refresh.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;;;;;;GAwBG;AASH,OAAO,EAAmC,KAAK,YAAY,EAAE,MAAM,YAAY,CAAC;AAChF,OAAO,EAAa,KAAK,gBAAgB,EAAE,MAAM,aAAa,CAAC;AAE/D,yDAAyD;AACzD,qBAAa,iBAAkB,SAAQ,KAAK;CAAG;AAO/C;;;;;;;;;;;;;;GAcG;AACH,wBAAgB,SAAS,CAAC,OAAO,EAAE,MAAM,GAAG,MAAM,EAAE,CAWnD;AAKD,MAAM,WAAW,SAAS;IACxB,sFAAsF;IACtF,IAAI,EAAE,MAAM,CAAC;IACb,8FAA8F;IAC9F,IAAI,EAAE,MAAM,GAAG,IAAI,CAAC;CACrB;AAED;;;;;;;;;;;GAWG;AACH,wBAAgB,YAAY,CAAC,OAAO,EAAE,MAAM,GAAG,SAAS,CAMvD;AAOD;;;;;;;GAOG;AACH,wBAAgB,UAAU,CAAC,MAAM,EAAE,MAAM,EAAE,YAAY,EAAE,MAAM,GAAG,MAAM,CAOvE;AAED,kGAAkG;AAClG,wBAAgB,OAAO,CAAC,MAAM,EAAE,MAAM,GAAG,MAAM,CAO9C;AAMD;;;;;;;;GAQG;AACH,wBAAgB,qBAAqB,CAAC,QAAQ,EAAE,MAAM,GAAG,OAAO,CA6B/D;AAcD,MAAM,WAAW,kBAAkB;IACjC,OAAO,EAAE,MAAM,CAAC;IAChB,QAAQ,EAAE,OAAO,CAAC;IAClB,IAAI,EAAE,MAAM,EAAE,CAAC;IACf,OAAO,EAAE,MAAM,CAAC;IAChB,sFAAsF;IACtF,YAAY,EAAE,OAAO,CAAC;CACvB;AA4CD,wBAAgB,kBAAkB,CAAC,KAAK,EAAE,kBAAkB,GAAG,MAAM,CAyCpE;AAED,MAAM,WAAW,aAAa;IAC5B,OAAO,EAAE,YAAY,CAAC;IACtB,uFAAuF;IACvF,SAAS,EAAE,OAAO,CAAC;IACnB,kFAAkF;IAClF,UAAU,EAAE,MAAM,EAAE,CAAC;CACtB;AAED,MAAM,WAAW,cAAe,SAAQ,gBAAgB;IACtD,4EAA4E;IAC5E,QAAQ,CAAC,EAAE,CAAC,MAAM,EAAE,MAAM,KAAK,OAAO,CAAC,MAAM,CAAC,CAAC;CAChD;AAED;;;;;;GAMG;AACH,wBAAsB,gBAAgB,CACpC,UAAU,EAAE,MAAM,EAClB,IAAI,GAAE,cAAmB,GACxB,OAAO,CAAC,aAAa,CAAC,CAiDxB"}
@@ -0,0 +1,333 @@
1
+ /**
2
+ * Task 179 — the Refresh button's actual work.
3
+ *
4
+ * Karl's three answers set the shape of this file:
5
+ *
6
+ * 1. *"Write it straight away, but commit to git first so I can undo."* — so a
7
+ * snapshot commit is a PRECONDITION, and a file outside a git repo is
8
+ * refused outright. The permission to write without asking was bought with
9
+ * the undo; without a repo there is no undo, so the permission lapses.
10
+ * 2. *"Both — convert to the new format AND re-check every task's state."* — one
11
+ * model call does both, because they are the same rewrite.
12
+ * 3. *"Build it as a general facility."* — which is `worker.ts`; this module is
13
+ * only its first caller.
14
+ *
15
+ * The load-bearing part is not the prompt. A model handed a whole file and asked
16
+ * for a whole file back will, sooner or later, quietly drop a row — so the answer
17
+ * is checked before it is written, and a run that loses a task id, fails to
18
+ * parse, or comes back without the marker is refused WHOLE. The git snapshot is
19
+ * the belt; these checks are the braces.
20
+ *
21
+ * Task 180, after the first live press: `## Done` is now split off before the
22
+ * prompt is built and re-attached after, and an empty queue is refused outright.
23
+ * See {@link splitOffDone} — the model was rewriting the one section the format
24
+ * says is never sent to a model, because it was the only thing left to touch.
25
+ */
26
+ import { execFileSync } from 'child_process';
27
+ import * as fs from 'fs';
28
+ import * as path from 'path';
29
+ import { TASKS_FORMAT_SECTION, hasTasksMarker, parseTasksFile } from '../lib/tasks-file.js';
30
+ import { getCurrentVersion } from '../lib/version-check.js';
31
+ import { readTasks, resolveRefreshTarget } from './tasks.js';
32
+ import { runWorker } from './worker.js';
33
+ /** A refusal with a reason the drawer shows verbatim. */
34
+ export class TasksRefreshError extends Error {
35
+ }
36
+ const SNAPSHOT_MESSAGE = 'chore(tasks): snapshot before refresh';
37
+ /** Task rows in either the schema format or the free-form one it replaced. */
38
+ const ANY_TASK_ID_RE = /^\s*-\s*\[[^\]]*\]\s*(\d{1,4})\b/;
39
+ /**
40
+ * Every task id the file currently carries.
41
+ *
42
+ * Deliberately permissive, and matched against the RAW text rather than a parse:
43
+ * the un-migrated files this exists to convert do not parse at all, and they are
44
+ * the ones with the most to lose. Both shapes seen in the wild are covered —
45
+ * `- [ ] 179 · title` (schema) and `- [◕] 178: title` (what came before).
46
+ *
47
+ * The `## Format` section is skipped, and that is not a nicety: the manual
48
+ * carried inside every migrated file demonstrates the grammar with EXAMPLE rows
49
+ * (`- [ ] 073 · StorePool reaping`, `- [⊘] 084 · Creative-writing benchmark`).
50
+ * Counting those as tasks would demand the model preserve four ids that do not
51
+ * exist, so every already-migrated file would fail the drop check and Refresh
52
+ * would work only on un-migrated ones. Found by test, not by reading.
53
+ */
54
+ export function taskIdsIn(content) {
55
+ const ids = [];
56
+ let inFormat = false;
57
+ for (const line of content.split('\n')) {
58
+ const heading = /^## +(.+?) *$/.exec(line);
59
+ if (heading)
60
+ inFormat = heading[1] === 'Format';
61
+ if (inFormat)
62
+ continue;
63
+ const m = ANY_TASK_ID_RE.exec(line);
64
+ if (m)
65
+ ids.push(m[1]);
66
+ }
67
+ return ids;
68
+ }
69
+ /** The `## Done` heading, alone on its line — the same shape the parser accepts. */
70
+ const DONE_HEADING_RE = /^## +Done *$/;
71
+ /**
72
+ * Split a tasks file at its `## Done` heading.
73
+ *
74
+ * Task 180: `## Done` is the record of work that has left the queue, and the
75
+ * format's own rule is that it is never sent to a model. The first live Refresh
76
+ * proved why that has to be enforced with scissors rather than with a sentence in
77
+ * the prompt: handed the whole file and an empty queue, the model spent its one
78
+ * call appending "the reload has happened" to twelve finished tasks.
79
+ *
80
+ * The LAST heading wins, because the manual carried inside every migrated file
81
+ * quotes `## Done` while describing the grammar.
82
+ */
83
+ export function splitOffDone(content) {
84
+ const lines = content.split('\n');
85
+ let at = -1;
86
+ for (let i = 0; i < lines.length; i++)
87
+ if (DONE_HEADING_RE.test(lines[i]))
88
+ at = i;
89
+ if (at < 0)
90
+ return { head: content, done: null };
91
+ return { head: lines.slice(0, at).join('\n'), done: lines.slice(at).join('\n') };
92
+ }
93
+ /** The rows under a `## Done` heading, with the heading and its blank padding gone. */
94
+ function doneBody(section) {
95
+ return section.split('\n').slice(1).join('\n').trim();
96
+ }
97
+ /**
98
+ * Re-attach the withheld record to the model's answer.
99
+ *
100
+ * The original section goes back byte-for-byte. If the model emitted a `## Done`
101
+ * of its own — it is told not to — those rows are spliced in ABOVE the preserved
102
+ * ones rather than discarded: dropping them would lose their ids, and the drop
103
+ * check would then refuse an answer whose only sin was disobedience.
104
+ */
105
+ export function stitchDone(answer, originalDone) {
106
+ const split = splitOffDone(answer);
107
+ const moved = split.done ? doneBody(split.done) : '';
108
+ const kept = doneBody(originalDone);
109
+ const heading = originalDone.split('\n', 1)[0];
110
+ const body = [moved, kept].filter(Boolean).join('\n');
111
+ return `${split.head.trimEnd()}\n\n${heading}\n\n${body}\n`;
112
+ }
113
+ /** Strip a ```-fenced wrapper, which a chat-tuned model adds however firmly it is told not to. */
114
+ export function unfence(answer) {
115
+ const trimmed = answer.trim();
116
+ if (!trimmed.startsWith('```'))
117
+ return trimmed;
118
+ const lines = trimmed.split('\n');
119
+ lines.shift();
120
+ if (lines[lines.length - 1]?.trim().startsWith('```'))
121
+ lines.pop();
122
+ return lines.join('\n').trim();
123
+ }
124
+ function git(cwd, args) {
125
+ return execFileSync('git', args, { cwd, encoding: 'utf-8', stdio: ['ignore', 'pipe', 'pipe'] });
126
+ }
127
+ /**
128
+ * Commit the file as it stands, and return whether there was anything to commit.
129
+ *
130
+ * Scoped by pathspec on BOTH commands, so a refresh never sweeps up whatever else
131
+ * the user happened to have staged. "Nothing to commit" is success, not failure:
132
+ * the file is already identical to HEAD, so HEAD *is* the undo point.
133
+ *
134
+ * Not being in a git repo is the one hard refusal here — see the module comment.
135
+ */
136
+ export function snapshotBeforeRefresh(filePath) {
137
+ const dir = path.dirname(filePath);
138
+ let root;
139
+ try {
140
+ root = git(dir, ['rev-parse', '--show-toplevel']).trim();
141
+ }
142
+ catch {
143
+ throw new TasksRefreshError(`Refresh needs a git repository to snapshot into, and ${filePath} is not in one. ` +
144
+ `It rewrites the whole file, so without an undo point it will not run.`);
145
+ }
146
+ const rel = path.relative(root, filePath);
147
+ try {
148
+ git(root, ['add', '--', rel]);
149
+ git(root, ['commit', '-m', SNAPSHOT_MESSAGE, '--', rel]);
150
+ return true;
151
+ }
152
+ catch (err) {
153
+ // git says "nothing to commit" on STDOUT and exits non-zero, so the throw is
154
+ // the success path here as often as it is the failure one.
155
+ const e = err;
156
+ const output = `${String(e.stdout ?? '')}\n${String(e.stderr ?? '')}`;
157
+ if (/nothing to commit|no changes added|nothing added to commit/i.test(output))
158
+ return false;
159
+ throw new TasksRefreshError(`Could not commit a snapshot of ${rel} before refreshing: ${err instanceof Error ? err.message : String(err)}`);
160
+ }
161
+ }
162
+ /** Filenames under `docs/tasks/`, so the model can attach `doc` fields it can verify exist. */
163
+ function taskDocListing(cwd) {
164
+ try {
165
+ return fs
166
+ .readdirSync(path.join(cwd, 'docs', 'tasks'))
167
+ .filter(name => name.endsWith('.md'))
168
+ .sort();
169
+ }
170
+ catch {
171
+ return [];
172
+ }
173
+ }
174
+ /**
175
+ * The whole prompt, as one pure function so the instructions are testable
176
+ * without a model.
177
+ *
178
+ * The three facts at the end are the ones the model cannot get from the file and
179
+ * would otherwise invent: which task docs exist, and what the gateway is actually
180
+ * running. That last one is the thing Karl complained about — rows still saying
181
+ * "needs reload" long after the reload happened.
182
+ */
183
+ /**
184
+ * Rules 1 and 6 — the two that change when the record is withheld: what to
185
+ * output, and where a finished task goes.
186
+ */
187
+ function promptShape(doneWithheld) {
188
+ if (doneWithheld) {
189
+ return {
190
+ rule1: [
191
+ '1. Output the file down to the END OF THE QUEUE and stop there: a `# <name> Tasks`',
192
+ ' title, the marker line `<!-- cumulus:tasks v1 -->` alone on its own line within',
193
+ ' the first ten lines, then `## Format` (verbatim as given above) and `## Queue`.',
194
+ ' Do NOT write a `## Done` section. The file has one; it is finished work, it was',
195
+ ' deliberately withheld from you, and it is re-attached unchanged after you answer.',
196
+ ].join('\n'),
197
+ rule6: [
198
+ '6. When a task is finished, mark its row `[✅]` and LEAVE IT IN THE QUEUE. Moving a',
199
+ " task out of the queue is the user's decision, taken in the drawer, not yours.",
200
+ ].join('\n'),
201
+ };
202
+ }
203
+ return {
204
+ rule1: [
205
+ '1. Output the whole file: a `# <name> Tasks` title, the marker line',
206
+ ' `<!-- cumulus:tasks v1 -->` alone on its own line within the first ten lines,',
207
+ ' then `## Format` (verbatim as given above), `## Queue`, and `## Done`.',
208
+ ].join('\n'),
209
+ rule6: [
210
+ '6. Finished work belongs in `## Done` with a `done <YYYY-MM-DD>` field. Work still',
211
+ ' to do belongs in `## Queue`, in build order, most important first.',
212
+ ].join('\n'),
213
+ };
214
+ }
215
+ export function buildRefreshPrompt(input) {
216
+ const docs = input.docs.length
217
+ ? input.docs.map(d => `- docs/tasks/${d}`).join('\n')
218
+ : '(no docs/tasks directory)';
219
+ const shape = promptShape(input.doneWithheld);
220
+ return [
221
+ 'You are rewriting a project task file into the exact format specified below.',
222
+ 'Return ONLY the complete new file. No preamble, no explanation, no code fence.',
223
+ '',
224
+ '=== THE FORMAT (this section must appear verbatim in your output) ===',
225
+ TASKS_FORMAT_SECTION,
226
+ '',
227
+ '=== RULES ===',
228
+ shape.rule1,
229
+ '2. EVERY task shown to you must appear in your output, with the SAME id.',
230
+ ' Losing an id fails the whole refresh and nothing is written. If a task has no',
231
+ ' id, mint one that does not collide with any other id in the file.',
232
+ '3. Prose belongs in `note` lines and nowhere else. A task row is one line.',
233
+ "4. Re-check each task's status against the evidence below. If you CANNOT ground a",
234
+ ' status change in that evidence, leave the status exactly as it is.',
235
+ '5. When you do change a status, add a `note` saying why, so the change is auditable.',
236
+ shape.rule6,
237
+ '',
238
+ '=== EVIDENCE YOU CANNOT GET FROM THE FILE ===',
239
+ `The gateway is currently running version ${input.version}. A QUEUED task whose note`,
240
+ 'claims its change is "published, needs reload" for a version at or below this one is',
241
+ 'stale — the reload has happened, and that is grounds to re-check its status. Do not',
242
+ 'rewrite a note merely to say so: an edit that changes no status is not worth making.',
243
+ '',
244
+ 'Task documents that exist on disk (use these for `doc` fields; do not invent paths):',
245
+ docs,
246
+ '',
247
+ input.doneWithheld
248
+ ? '=== THE CURRENT FILE, DOWN TO THE END OF THE QUEUE (its `## Done` is withheld) ==='
249
+ : input.migrated
250
+ ? '=== THE CURRENT FILE (already in this format — re-check it) ==='
251
+ : '=== THE CURRENT FILE (free-form — convert it) ===',
252
+ input.content,
253
+ ].join('\n');
254
+ }
255
+ /**
256
+ * Convert and re-check a thread's tasks file in place.
257
+ *
258
+ * Order matters and is the design: resolve → snapshot → ask → CHECK → write.
259
+ * Nothing between the snapshot and the write touches disk, so every refusal below
260
+ * leaves the file byte-identical to what was just committed.
261
+ */
262
+ export async function refreshTasksFile(threadName, opts = {}) {
263
+ const target = await resolveRefreshTarget(threadName);
264
+ if (!target) {
265
+ throw new TasksRefreshError('This thread has no Tasks.md in its always-include list, so there is nothing to refresh.');
266
+ }
267
+ // Task 180: nothing in the queue, nothing to refresh. Refused BEFORE the
268
+ // snapshot and the model call, because the alternative is what happened on the
269
+ // first live press — a model with no legal work finding some anyway. A file
270
+ // that fails to parse is exempt: a queue that reads as empty because the file
271
+ // is broken is precisely the case Refresh exists to fix.
272
+ if (target.migrated) {
273
+ const current = parseTasksFile(target.content);
274
+ if (current.queue.length === 0 && current.errors.length === 0) {
275
+ throw new TasksRefreshError('Nothing to refresh — the queue is empty. Finished work in `## Done` is the record ' +
276
+ 'and is left alone.');
277
+ }
278
+ }
279
+ const committed = snapshotBeforeRefresh(target.filePath);
280
+ // The record is split off rather than trusted to the prompt — see splitOffDone.
281
+ const split = target.migrated
282
+ ? splitOffDone(target.content)
283
+ : { head: target.content, done: null };
284
+ const prompt = buildRefreshPrompt({
285
+ content: split.done ? split.head : target.content,
286
+ migrated: target.migrated,
287
+ docs: taskDocListing(target.cwd),
288
+ version: getCurrentVersion(),
289
+ doneWithheld: split.done !== null,
290
+ });
291
+ const raw = opts.runModel
292
+ ? await opts.runModel(prompt)
293
+ : await runWorker(threadName, 'tasks-refresh', prompt, opts);
294
+ const answer = unfence(raw);
295
+ const next = split.done ? stitchDone(answer, split.done) : answer;
296
+ assertUsable(answer, next, target.content);
297
+ fs.writeFileSync(target.filePath, next.endsWith('\n') ? next : `${next}\n`, 'utf-8');
298
+ return { payload: await readTasks(threadName), committed, droppedIds: [] };
299
+ }
300
+ /**
301
+ * The guard rails. Each one refuses the WHOLE answer; none patches it up, and
302
+ * every one throws before the single `writeFileSync` above runs, so a refusal
303
+ * leaves the file byte-identical to what the snapshot just committed.
304
+ */
305
+ function assertUsable(answer, next, original) {
306
+ // Emptiness is judged on the ANSWER, not the stitched file: re-attaching the
307
+ // record to nothing would produce a plausible-looking file with no queue.
308
+ if (!answer) {
309
+ throw new TasksRefreshError('The worker returned an empty file. Nothing was written.');
310
+ }
311
+ if (!hasTasksMarker(next)) {
312
+ throw new TasksRefreshError("The worker's answer has no `<!-- cumulus:tasks v1 -->` marker on its own line in the " +
313
+ 'first ten lines, so it is not a tasks file. Nothing was written.');
314
+ }
315
+ const parsed = parseTasksFile(next);
316
+ if (parsed.errors.length > 0) {
317
+ const first = parsed.errors[0];
318
+ throw new TasksRefreshError(`The worker's answer does not parse (${parsed.errors.length} bad line${parsed.errors.length === 1 ? '' : 's'}, first at line ${first.line}: ${first.reason}). Nothing was written.`);
319
+ }
320
+ // The one check that catches the failure this whole task is built around: a
321
+ // wholesale rewrite that silently loses a row. Compared against the RAW ids of
322
+ // the file that went in, so it holds for un-migrated files too.
323
+ const before = new Set(taskIdsIn(original));
324
+ const after = new Set(parsed.queue.concat(parsed.done).map(row => row.id));
325
+ const droppedIds = [...before].filter(id => !after.has(id));
326
+ if (droppedIds.length > 0) {
327
+ throw new TasksRefreshError(`The worker dropped ${droppedIds.length} task${droppedIds.length === 1 ? '' : 's'} (${droppedIds.join(', ')}). Nothing was written — the file is exactly as it was.`);
328
+ }
329
+ if (after.size === 0 && before.size > 0) {
330
+ throw new TasksRefreshError('The worker returned a file with no tasks in it. Nothing was written.');
331
+ }
332
+ }
333
+ //# sourceMappingURL=tasks-refresh.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"tasks-refresh.js","sourceRoot":"","sources":["../../src/gateway/tasks-refresh.ts"],"names":[],"mappings":"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"}
@@ -20,6 +20,24 @@ export interface ResolvedTasksFile {
20
20
  * under any other name is.
21
21
  */
22
22
  export declare function resolveTasksFile(threadName: string): Promise<ResolvedTasksFile | null>;
23
+ /** What task 179's Refresh is pointed at, and whether it has been migrated yet. */
24
+ export interface RefreshTarget {
25
+ filePath: string;
26
+ content: string;
27
+ cwd: string;
28
+ /** True when the file already carries the marker — Refresh re-checks rather than converts. */
29
+ migrated: boolean;
30
+ }
31
+ /**
32
+ * The file Refresh acts on (task 179).
33
+ *
34
+ * A superset of {@link resolveTasksFile}: a marked file wins wherever it sits,
35
+ * and failing that, an always-included file *named* `Tasks.md` — which is
36
+ * precisely the un-migrated case Refresh exists to convert. Filename is the
37
+ * fallback ONLY here; the prompt projection still keys on the marker alone, so
38
+ * conversion never happens by accident on a file nobody pointed at.
39
+ */
40
+ export declare function resolveRefreshTarget(threadName: string): Promise<RefreshTarget | null>;
23
41
  /** Wire shape for the drawer — flat rows, depth carried explicitly. */
24
42
  export interface TasksPayload {
25
43
  filePath: string | null;
@@ -1 +1 @@
1
- {"version":3,"file":"tasks.d.ts","sourceRoot":"","sources":["../../src/gateway/tasks.ts"],"names":[],"mappings":"AAAA;;;;;;;;GAQG;AAYH,OAAO,EACL,eAAe,EACf,OAAO,EACP,SAAS,EASV,MAAM,sBAAsB,CAAC;AAG9B,MAAM,WAAW,iBAAiB;IAChC,QAAQ,EAAE,MAAM,CAAC;IACjB,IAAI,EAAE,SAAS,CAAC;CACjB;AAED;;;;;;GAMG;AACH,wBAAsB,gBAAgB,CAAC,UAAU,EAAE,MAAM,GAAG,OAAO,CAAC,iBAAiB,GAAG,IAAI,CAAC,CAkB5F;AAED,uEAAuE;AACvE,MAAM,WAAW,YAAY;IAC3B,QAAQ,EAAE,MAAM,GAAG,IAAI,CAAC;IACxB,KAAK,EAAE,OAAO,EAAE,CAAC;IACjB,IAAI,EAAE,OAAO,EAAE,CAAC;IAChB,MAAM,EAAE,SAAS,CAAC,QAAQ,CAAC,CAAC;IAC5B,mEAAmE;IACnE,OAAO,CAAC,EAAE,OAAO,CAAC;CACnB;AAED,wBAAsB,SAAS,CAAC,UAAU,EAAE,MAAM,GAAG,OAAO,CAAC,YAAY,CAAC,CAWzE;AAgDD,wBAAgB,gBAAgB,CAC9B,UAAU,EAAE,MAAM,EAClB,MAAM,EAAE,MAAM,EACd,QAAQ,EAAE,MAAM,EAChB,QAAQ,EAAE,QAAQ,GAAG,OAAO,GAC3B,OAAO,CAAC,YAAY,CAAC,CAEvB;AAED,wBAAgB,kBAAkB,CAChC,UAAU,EAAE,MAAM,EAClB,EAAE,EAAE,MAAM,EACV,MAAM,EAAE,OAAO,GACd,OAAO,CAAC,YAAY,CAAC,CAEvB;AAED,0DAA0D;AAC1D,wBAAgB,wBAAwB,CACtC,UAAU,EAAE,MAAM,EAClB,GAAG,EAAE,MAAM,EAAE,EACb,WAAW,EAAE,eAAe,GAC3B,OAAO,CAAC,YAAY,CAAC,CAEvB"}
1
+ {"version":3,"file":"tasks.d.ts","sourceRoot":"","sources":["../../src/gateway/tasks.ts"],"names":[],"mappings":"AAAA;;;;;;;;GAQG;AAYH,OAAO,EACL,eAAe,EACf,OAAO,EACP,SAAS,EASV,MAAM,sBAAsB,CAAC;AAG9B,MAAM,WAAW,iBAAiB;IAChC,QAAQ,EAAE,MAAM,CAAC;IACjB,IAAI,EAAE,SAAS,CAAC;CACjB;AAmCD;;;;;;GAMG;AACH,wBAAsB,gBAAgB,CAAC,UAAU,EAAE,MAAM,GAAG,OAAO,CAAC,iBAAiB,GAAG,IAAI,CAAC,CAO5F;AAED,mFAAmF;AACnF,MAAM,WAAW,aAAa;IAC5B,QAAQ,EAAE,MAAM,CAAC;IACjB,OAAO,EAAE,MAAM,CAAC;IAChB,GAAG,EAAE,MAAM,CAAC;IACZ,8FAA8F;IAC9F,QAAQ,EAAE,OAAO,CAAC;CACnB;AAED;;;;;;;;GAQG;AACH,wBAAsB,oBAAoB,CAAC,UAAU,EAAE,MAAM,GAAG,OAAO,CAAC,aAAa,GAAG,IAAI,CAAC,CAW5F;AAED,uEAAuE;AACvE,MAAM,WAAW,YAAY;IAC3B,QAAQ,EAAE,MAAM,GAAG,IAAI,CAAC;IACxB,KAAK,EAAE,OAAO,EAAE,CAAC;IACjB,IAAI,EAAE,OAAO,EAAE,CAAC;IAChB,MAAM,EAAE,SAAS,CAAC,QAAQ,CAAC,CAAC;IAC5B,mEAAmE;IACnE,OAAO,CAAC,EAAE,OAAO,CAAC;CACnB;AAED,wBAAsB,SAAS,CAAC,UAAU,EAAE,MAAM,GAAG,OAAO,CAAC,YAAY,CAAC,CAWzE;AAgDD,wBAAgB,gBAAgB,CAC9B,UAAU,EAAE,MAAM,EAClB,MAAM,EAAE,MAAM,EACd,QAAQ,EAAE,MAAM,EAChB,QAAQ,EAAE,QAAQ,GAAG,OAAO,GAC3B,OAAO,CAAC,YAAY,CAAC,CAEvB;AAED,wBAAgB,kBAAkB,CAChC,UAAU,EAAE,MAAM,EAClB,EAAE,EAAE,MAAM,EACV,MAAM,EAAE,OAAO,GACd,OAAO,CAAC,YAAY,CAAC,CAEvB;AAED,0DAA0D;AAC1D,wBAAgB,wBAAwB,CACtC,UAAU,EAAE,MAAM,EAClB,GAAG,EAAE,MAAM,EAAE,EACb,WAAW,EAAE,eAAe,GAC3B,OAAO,CAAC,YAAY,CAAC,CAEvB"}
@@ -14,33 +14,68 @@ import { loadGlobalConfig, loadThreadConfig, mergeConfigs, resolveFilePath, } fr
14
14
  import { TasksFileError, assertWritable, dispositionTasks, hasTasksMarker, moveTask, parseTasksFile, resolveProposedDep, serializeTasksFile, } from '../lib/tasks-file.js';
15
15
  import { resolveThreadCwd } from '../lib/worktree.js';
16
16
  /**
17
- * Find the thread's schema tasks file, or null when it has none.
17
+ * The thread's always-include files, in configured order, with their contents.
18
18
  *
19
- * Detection is the marker inside the file, never the filename: a project whose
20
- * `Tasks.md` has not been migrated is not a tasks file, and a migrated file
21
- * under any other name is.
19
+ * The single place that answers "which files could be this thread's task list",
20
+ * so the marker rule below and task 179's refresh target cannot resolve against
21
+ * different sets (Rule #8).
22
22
  */
23
- export async function resolveTasksFile(threadName) {
23
+ async function threadIncludeFiles(threadName) {
24
24
  const merged = mergeConfigs(await loadGlobalConfig(), await loadThreadConfig(threadName));
25
25
  const resolved = await resolveThreadCwd(threadName, merged.projectDir);
26
26
  const cwd = resolved.cwd || merged.projectDir || path.join(os.homedir(), 'projects', threadName);
27
+ const files = [];
27
28
  for (const entry of merged.alwaysInclude ?? []) {
28
29
  const filePath = resolveFilePath(entry, cwd);
29
- let content;
30
30
  try {
31
31
  if (!fs.existsSync(filePath) || fs.statSync(filePath).isDirectory())
32
32
  continue;
33
- content = fs.readFileSync(filePath, 'utf-8');
33
+ files.push({ filePath, content: fs.readFileSync(filePath, 'utf-8') });
34
34
  }
35
35
  catch {
36
36
  continue;
37
37
  }
38
+ }
39
+ return { cwd, files };
40
+ }
41
+ /**
42
+ * Find the thread's schema tasks file, or null when it has none.
43
+ *
44
+ * Detection is the marker inside the file, never the filename: a project whose
45
+ * `Tasks.md` has not been migrated is not a tasks file, and a migrated file
46
+ * under any other name is.
47
+ */
48
+ export async function resolveTasksFile(threadName) {
49
+ const { files } = await threadIncludeFiles(threadName);
50
+ for (const { filePath, content } of files) {
38
51
  if (!hasTasksMarker(content))
39
52
  continue;
40
53
  return { filePath, file: parseTasksFile(content) };
41
54
  }
42
55
  return null;
43
56
  }
57
+ /**
58
+ * The file Refresh acts on (task 179).
59
+ *
60
+ * A superset of {@link resolveTasksFile}: a marked file wins wherever it sits,
61
+ * and failing that, an always-included file *named* `Tasks.md` — which is
62
+ * precisely the un-migrated case Refresh exists to convert. Filename is the
63
+ * fallback ONLY here; the prompt projection still keys on the marker alone, so
64
+ * conversion never happens by accident on a file nobody pointed at.
65
+ */
66
+ export async function resolveRefreshTarget(threadName) {
67
+ const { cwd, files } = await threadIncludeFiles(threadName);
68
+ for (const { filePath, content } of files) {
69
+ if (hasTasksMarker(content))
70
+ return { filePath, content, cwd, migrated: true };
71
+ }
72
+ for (const { filePath, content } of files) {
73
+ if (path.basename(filePath).toLowerCase() === 'tasks.md') {
74
+ return { filePath, content, cwd, migrated: false };
75
+ }
76
+ }
77
+ return null;
78
+ }
44
79
  export async function readTasks(threadName) {
45
80
  const resolved = await resolveTasksFile(threadName);
46
81
  if (!resolved) {
@@ -1 +1 @@
1
- {"version":3,"file":"tasks.js","sourceRoot":"","sources":["../../src/gateway/tasks.ts"],"names":[],"mappings":"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"}
1
+ {"version":3,"file":"tasks.js","sourceRoot":"","sources":["../../src/gateway/tasks.ts"],"names":[],"mappings":"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"}