reflectt-node 0.1.6 → 0.1.8

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 (85) hide show
  1. package/README.md +13 -0
  2. package/defaults/gitignore.template +23 -0
  3. package/dist/boardHealthWorker.d.ts +4 -0
  4. package/dist/boardHealthWorker.d.ts.map +1 -1
  5. package/dist/boardHealthWorker.js +36 -1
  6. package/dist/boardHealthWorker.js.map +1 -1
  7. package/dist/buildInfo.d.ts.map +1 -1
  8. package/dist/buildInfo.js +47 -10
  9. package/dist/buildInfo.js.map +1 -1
  10. package/dist/chat.d.ts +4 -0
  11. package/dist/chat.d.ts.map +1 -1
  12. package/dist/chat.js +6 -2
  13. package/dist/chat.js.map +1 -1
  14. package/dist/cli.js +37 -12
  15. package/dist/cli.js.map +1 -1
  16. package/dist/cloud.d.ts.map +1 -1
  17. package/dist/cloud.js +131 -64
  18. package/dist/cloud.js.map +1 -1
  19. package/dist/continuity-loop.d.ts.map +1 -1
  20. package/dist/continuity-loop.js +297 -29
  21. package/dist/continuity-loop.js.map +1 -1
  22. package/dist/dashboard.js +1 -1
  23. package/dist/dashboard.js.map +1 -1
  24. package/dist/deploy-monitor.d.ts +18 -0
  25. package/dist/deploy-monitor.d.ts.map +1 -0
  26. package/dist/deploy-monitor.js +165 -0
  27. package/dist/deploy-monitor.js.map +1 -0
  28. package/dist/events.d.ts.map +1 -1
  29. package/dist/events.js +15 -2
  30. package/dist/events.js.map +1 -1
  31. package/dist/executionSweeper.d.ts +1 -0
  32. package/dist/executionSweeper.d.ts.map +1 -1
  33. package/dist/executionSweeper.js +43 -7
  34. package/dist/executionSweeper.js.map +1 -1
  35. package/dist/files.d.ts.map +1 -1
  36. package/dist/files.js +17 -3
  37. package/dist/files.js.map +1 -1
  38. package/dist/fingerprint.d.ts +30 -0
  39. package/dist/fingerprint.d.ts.map +1 -0
  40. package/dist/fingerprint.js +122 -0
  41. package/dist/fingerprint.js.map +1 -0
  42. package/dist/github-webhook-attribution.d.ts +38 -0
  43. package/dist/github-webhook-attribution.d.ts.map +1 -0
  44. package/dist/github-webhook-attribution.js +123 -0
  45. package/dist/github-webhook-attribution.js.map +1 -0
  46. package/dist/inbox.d.ts.map +1 -1
  47. package/dist/inbox.js +4 -0
  48. package/dist/inbox.js.map +1 -1
  49. package/dist/index.js +37 -1
  50. package/dist/index.js.map +1 -1
  51. package/dist/pulse.d.ts +7 -0
  52. package/dist/pulse.d.ts.map +1 -1
  53. package/dist/pulse.js +15 -0
  54. package/dist/pulse.js.map +1 -1
  55. package/dist/review-state.d.ts +9 -0
  56. package/dist/review-state.d.ts.map +1 -0
  57. package/dist/review-state.js +17 -0
  58. package/dist/review-state.js.map +1 -0
  59. package/dist/schedule.d.ts +60 -0
  60. package/dist/schedule.d.ts.map +1 -0
  61. package/dist/schedule.js +176 -0
  62. package/dist/schedule.js.map +1 -0
  63. package/dist/server.d.ts.map +1 -1
  64. package/dist/server.js +501 -14
  65. package/dist/server.js.map +1 -1
  66. package/dist/suppression-ledger.d.ts.map +1 -1
  67. package/dist/suppression-ledger.js +12 -3
  68. package/dist/suppression-ledger.js.map +1 -1
  69. package/dist/system-loop-state.d.ts +1 -1
  70. package/dist/system-loop-state.d.ts.map +1 -1
  71. package/dist/system-loop-state.js +1 -0
  72. package/dist/system-loop-state.js.map +1 -1
  73. package/dist/tasks.d.ts +9 -1
  74. package/dist/tasks.d.ts.map +1 -1
  75. package/dist/tasks.js +193 -41
  76. package/dist/tasks.js.map +1 -1
  77. package/dist/types.d.ts +1 -1
  78. package/dist/types.d.ts.map +1 -1
  79. package/dist/usage-tracking.d.ts +26 -0
  80. package/dist/usage-tracking.d.ts.map +1 -1
  81. package/dist/usage-tracking.js +91 -4
  82. package/dist/usage-tracking.js.map +1 -1
  83. package/package.json +1 -1
  84. package/public/dashboard.js +136 -56
  85. package/public/docs.md +18 -0
@@ -1 +1 @@
1
- {"version":3,"file":"suppression-ledger.d.ts","sourceRoot":"","sources":["../src/suppression-ledger.ts"],"names":[],"mappings":"AASA,MAAM,WAAW,gBAAgB;IAC/B,EAAE,EAAE,MAAM,CAAA;IACV,SAAS,EAAE,MAAM,CAAA;IACjB,QAAQ,EAAE,MAAM,CAAA;IAChB,OAAO,EAAE,MAAM,CAAA;IACf,IAAI,EAAE,MAAM,CAAA;IACZ,eAAe,EAAE,MAAM,GAAG,IAAI,CAAA;IAC9B,aAAa,EAAE,MAAM,CAAA;IACrB,YAAY,EAAE,MAAM,CAAA;IACpB,SAAS,EAAE,MAAM,CAAA;IACjB,UAAU,EAAE,OAAO,CAAA;IACnB,SAAS,EAAE,MAAM,CAAA;CAClB;AAED,MAAM,WAAW,sBAAsB;IACrC,WAAW,EAAE,OAAO,CAAA;IACpB,SAAS,EAAE,MAAM,CAAA;IACjB,QAAQ,CAAC,EAAE,gBAAgB,CAAA;CAC5B;AAED,MAAM,WAAW,gBAAgB;IAC/B,aAAa,EAAE,MAAM,CAAA;IACrB,gBAAgB,EAAE,MAAM,CAAA;IACxB,UAAU,EAAE,MAAM,CAAA;IAClB,WAAW,EAAE,MAAM,CAAC,MAAM,EAAE;QAAE,OAAO,EAAE,MAAM,CAAC;QAAC,UAAU,EAAE,MAAM,CAAC;QAAC,IAAI,EAAE,MAAM,CAAA;KAAE,CAAC,CAAA;IAClF,UAAU,EAAE,MAAM,CAAC,MAAM,EAAE;QAAE,OAAO,EAAE,MAAM,CAAC;QAAC,UAAU,EAAE,MAAM,CAAC;QAAC,IAAI,EAAE,MAAM,CAAA;KAAE,CAAC,CAAA;IACjF,SAAS,EAAE,MAAM,CAAA;IACjB,cAAc,EAAE,MAAM,CAAA;CACvB;AAID,qBAAa,iBAAiB;IAC5B,OAAO,CAAC,QAAQ,CAAQ;gBAEZ,QAAQ,CAAC,EAAE,MAAM;IAI7B;;;OAGG;IACH,eAAe,CAAC,QAAQ,EAAE,MAAM,EAAE,OAAO,EAAE,MAAM,EAAE,OAAO,EAAE,MAAM,GAAG,MAAM;IAY3E;;;;OAIG;IACH,KAAK,CAAC,IAAI,EAAE;QACV,QAAQ,EAAE,MAAM,CAAA;QAChB,OAAO,EAAE,MAAM,CAAA;QACf,IAAI,EAAE,MAAM,CAAA;QACZ,OAAO,EAAE,MAAM,CAAA;KAChB,GAAG,sBAAsB;IAyD1B;;OAEG;IACH,QAAQ,IAAI,gBAAgB;IAqC5B;;OAEG;IACH,KAAK,IAAI,MAAM;IAOf;;OAEG;IACH,WAAW,IAAI,MAAM;IAIrB;;OAEG;IACH,WAAW,CAAC,EAAE,EAAE,MAAM,GAAG,IAAI;CAG9B;AAED,eAAO,MAAM,iBAAiB,mBAA0B,CAAA"}
1
+ {"version":3,"file":"suppression-ledger.d.ts","sourceRoot":"","sources":["../src/suppression-ledger.ts"],"names":[],"mappings":"AASA,MAAM,WAAW,gBAAgB;IAC/B,EAAE,EAAE,MAAM,CAAA;IACV,SAAS,EAAE,MAAM,CAAA;IACjB,QAAQ,EAAE,MAAM,CAAA;IAChB,OAAO,EAAE,MAAM,CAAA;IACf,IAAI,EAAE,MAAM,CAAA;IACZ,eAAe,EAAE,MAAM,GAAG,IAAI,CAAA;IAC9B,aAAa,EAAE,MAAM,CAAA;IACrB,YAAY,EAAE,MAAM,CAAA;IACpB,SAAS,EAAE,MAAM,CAAA;IACjB,UAAU,EAAE,OAAO,CAAA;IACnB,SAAS,EAAE,MAAM,CAAA;CAClB;AAED,MAAM,WAAW,sBAAsB;IACrC,WAAW,EAAE,OAAO,CAAA;IACpB,SAAS,EAAE,MAAM,CAAA;IACjB,QAAQ,CAAC,EAAE,gBAAgB,CAAA;CAC5B;AAED,MAAM,WAAW,gBAAgB;IAC/B,aAAa,EAAE,MAAM,CAAA;IACrB,gBAAgB,EAAE,MAAM,CAAA;IACxB,UAAU,EAAE,MAAM,CAAA;IAClB,WAAW,EAAE,MAAM,CAAC,MAAM,EAAE;QAAE,OAAO,EAAE,MAAM,CAAC;QAAC,UAAU,EAAE,MAAM,CAAC;QAAC,IAAI,EAAE,MAAM,CAAA;KAAE,CAAC,CAAA;IAClF,UAAU,EAAE,MAAM,CAAC,MAAM,EAAE;QAAE,OAAO,EAAE,MAAM,CAAC;QAAC,UAAU,EAAE,MAAM,CAAC;QAAC,IAAI,EAAE,MAAM,CAAA;KAAE,CAAC,CAAA;IACjF,SAAS,EAAE,MAAM,CAAA;IACjB,cAAc,EAAE,MAAM,CAAA;CACvB;AAID,qBAAa,iBAAiB;IAC5B,OAAO,CAAC,QAAQ,CAAQ;gBAEZ,QAAQ,CAAC,EAAE,MAAM;IAI7B;;;OAGG;IACH,eAAe,CAAC,QAAQ,EAAE,MAAM,EAAE,OAAO,EAAE,MAAM,EAAE,OAAO,EAAE,MAAM,GAAG,MAAM;IAuB3E;;;;OAIG;IACH,KAAK,CAAC,IAAI,EAAE;QACV,QAAQ,EAAE,MAAM,CAAA;QAChB,OAAO,EAAE,MAAM,CAAA;QACf,IAAI,EAAE,MAAM,CAAA;QACZ,OAAO,EAAE,MAAM,CAAA;KAChB,GAAG,sBAAsB;IAyD1B;;OAEG;IACH,QAAQ,IAAI,gBAAgB;IAqC5B;;OAEG;IACH,KAAK,IAAI,MAAM;IAOf;;OAEG;IACH,WAAW,IAAI,MAAM;IAIrB;;OAEG;IACH,WAAW,CAAC,EAAE,EAAE,MAAM,GAAG,IAAI;CAG9B;AAED,eAAO,MAAM,iBAAiB,mBAA0B,CAAA"}
@@ -16,13 +16,22 @@ export class SuppressionLedger {
16
16
  * Content is normalized: timestamps, task IDs, and message IDs stripped.
17
17
  */
18
18
  computeDedupKey(category, channel, content) {
19
- const normalized = content
19
+ let normalized = content
20
20
  .trim()
21
21
  .toLowerCase()
22
22
  .replace(/\b(msg-|task-|tcomment-|ins-|ref-)\S+/g, '')
23
23
  .replace(/\d{13,}/g, '')
24
- .replace(/\s+/g, ' ')
25
- .slice(0, 300);
24
+ .replace(/\s+/g, ' ');
25
+ // For digest messages, aggressively strip volatile counts so that
26
+ // "32 todo · 2 doing" vs "31 todo · 3 doing" hashes identically.
27
+ // This prevents process restarts from re-emitting the same digest
28
+ // just because task counts shifted by 1-2.
29
+ if (category === 'digest') {
30
+ normalized = normalized
31
+ .replace(/\d+/g, 'N') // normalize all remaining numbers
32
+ .replace(/\bN+\b/g, 'N'); // collapse repeated N
33
+ }
34
+ normalized = normalized.slice(0, 300);
26
35
  const raw = `${category}:${channel}:${normalized}`;
27
36
  return createHash('sha256').update(raw).digest('hex').substring(0, 20);
28
37
  }
@@ -1 +1 @@
1
- {"version":3,"file":"suppression-ledger.js","sourceRoot":"","sources":["../src/suppression-ledger.ts"],"names":[],"mappings":"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"}
1
+ {"version":3,"file":"suppression-ledger.js","sourceRoot":"","sources":["../src/suppression-ledger.ts"],"names":[],"mappings":"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"}
@@ -1,4 +1,4 @@
1
- export type SystemLoopName = 'idle_nudge' | 'cadence_watchdog' | 'mention_rescue' | 'reflection_pipeline' | 'board_health';
1
+ export type SystemLoopName = 'idle_nudge' | 'cadence_watchdog' | 'mention_rescue' | 'reflection_pipeline' | 'board_health' | 'deploy_monitor';
2
2
  export declare function recordSystemLoopTick(name: SystemLoopName, now?: number): void;
3
3
  export declare function getSystemLoopTicks(): Record<SystemLoopName, number>;
4
4
  //# sourceMappingURL=system-loop-state.d.ts.map
@@ -1 +1 @@
1
- {"version":3,"file":"system-loop-state.d.ts","sourceRoot":"","sources":["../src/system-loop-state.ts"],"names":[],"mappings":"AASA,MAAM,MAAM,cAAc,GACtB,YAAY,GACZ,kBAAkB,GAClB,gBAAgB,GAChB,qBAAqB,GACrB,cAAc,CAAA;AAElB,wBAAgB,oBAAoB,CAAC,IAAI,EAAE,cAAc,EAAE,GAAG,SAAa,GAAG,IAAI,CAWjF;AAED,wBAAgB,kBAAkB,IAAI,MAAM,CAAC,cAAc,EAAE,MAAM,CAAC,CAqBnE"}
1
+ {"version":3,"file":"system-loop-state.d.ts","sourceRoot":"","sources":["../src/system-loop-state.ts"],"names":[],"mappings":"AASA,MAAM,MAAM,cAAc,GACtB,YAAY,GACZ,kBAAkB,GAClB,gBAAgB,GAChB,qBAAqB,GACrB,cAAc,GACd,gBAAgB,CAAA;AAEpB,wBAAgB,oBAAoB,CAAC,IAAI,EAAE,cAAc,EAAE,GAAG,SAAa,GAAG,IAAI,CAWjF;AAED,wBAAgB,kBAAkB,IAAI,MAAM,CAAC,cAAc,EAAE,MAAM,CAAC,CAsBnE"}
@@ -23,6 +23,7 @@ export function getSystemLoopTicks() {
23
23
  mention_rescue: 0,
24
24
  reflection_pipeline: 0,
25
25
  board_health: 0,
26
+ deploy_monitor: 0,
26
27
  };
27
28
  try {
28
29
  const rows = db.prepare('SELECT name, last_tick_at FROM system_loop_ticks').all();
@@ -1 +1 @@
1
- {"version":3,"file":"system-loop-state.js","sourceRoot":"","sources":["../src/system-loop-state.ts"],"names":[],"mappings":"AAAA,sCAAsC;AAEtC;;;GAGG;AAEH,OAAO,EAAE,KAAK,EAAE,MAAM,SAAS,CAAA;AAS/B,MAAM,UAAU,oBAAoB,CAAC,IAAoB,EAAE,GAAG,GAAG,IAAI,CAAC,GAAG,EAAE;IACzE,MAAM,EAAE,GAAG,KAAK,EAAE,CAAA;IAClB,IAAI,CAAC;QACH,EAAE,CAAC,OAAO,CACR;;4EAEsE,CACvE,CAAC,GAAG,CAAC,IAAI,EAAE,GAAG,CAAC,CAAA;IAClB,CAAC;IAAC,MAAM,CAAC;QACP,iEAAiE;IACnE,CAAC;AACH,CAAC;AAED,MAAM,UAAU,kBAAkB;IAChC,MAAM,EAAE,GAAG,KAAK,EAAE,CAAA;IAClB,MAAM,GAAG,GAAmC;QAC1C,UAAU,EAAE,CAAC;QACb,gBAAgB,EAAE,CAAC;QACnB,cAAc,EAAE,CAAC;QACjB,mBAAmB,EAAE,CAAC;QACtB,YAAY,EAAE,CAAC;KAChB,CAAA;IAED,IAAI,CAAC;QACH,MAAM,IAAI,GAAG,EAAE,CAAC,OAAO,CAAC,kDAAkD,CAAC,CAAC,GAAG,EAAmD,CAAA;QAClI,KAAK,MAAM,CAAC,IAAI,IAAI,EAAE,CAAC;YACrB,MAAM,IAAI,GAAG,MAAM,CAAC,CAAC,CAAC,IAAI,CAAmB,CAAA;YAC7C,IAAI,IAAI,IAAI,GAAG;gBAAE,GAAG,CAAC,IAAI,CAAC,GAAG,MAAM,CAAC,CAAC,CAAC,YAAY,IAAI,CAAC,CAAC,CAAA;QAC1D,CAAC;IACH,CAAC;IAAC,MAAM,CAAC;QACP,SAAS;IACX,CAAC;IAED,OAAO,GAAG,CAAA;AACZ,CAAC"}
1
+ {"version":3,"file":"system-loop-state.js","sourceRoot":"","sources":["../src/system-loop-state.ts"],"names":[],"mappings":"AAAA,sCAAsC;AAEtC;;;GAGG;AAEH,OAAO,EAAE,KAAK,EAAE,MAAM,SAAS,CAAA;AAU/B,MAAM,UAAU,oBAAoB,CAAC,IAAoB,EAAE,GAAG,GAAG,IAAI,CAAC,GAAG,EAAE;IACzE,MAAM,EAAE,GAAG,KAAK,EAAE,CAAA;IAClB,IAAI,CAAC;QACH,EAAE,CAAC,OAAO,CACR;;4EAEsE,CACvE,CAAC,GAAG,CAAC,IAAI,EAAE,GAAG,CAAC,CAAA;IAClB,CAAC;IAAC,MAAM,CAAC;QACP,iEAAiE;IACnE,CAAC;AACH,CAAC;AAED,MAAM,UAAU,kBAAkB;IAChC,MAAM,EAAE,GAAG,KAAK,EAAE,CAAA;IAClB,MAAM,GAAG,GAAmC;QAC1C,UAAU,EAAE,CAAC;QACb,gBAAgB,EAAE,CAAC;QACnB,cAAc,EAAE,CAAC;QACjB,mBAAmB,EAAE,CAAC;QACtB,YAAY,EAAE,CAAC;QACf,cAAc,EAAE,CAAC;KAClB,CAAA;IAED,IAAI,CAAC;QACH,MAAM,IAAI,GAAG,EAAE,CAAC,OAAO,CAAC,kDAAkD,CAAC,CAAC,GAAG,EAAmD,CAAA;QAClI,KAAK,MAAM,CAAC,IAAI,IAAI,EAAE,CAAC;YACrB,MAAM,IAAI,GAAG,MAAM,CAAC,CAAC,CAAC,IAAI,CAAmB,CAAA;YAC7C,IAAI,IAAI,IAAI,GAAG;gBAAE,GAAG,CAAC,IAAI,CAAC,GAAG,MAAM,CAAC,CAAC,CAAC,YAAY,IAAI,CAAC,CAAC,CAAA;QAC1D,CAAC;IACH,CAAC;IAAC,MAAM,CAAC;QACP,SAAS;IACX,CAAC;IAED,OAAO,GAAG,CAAA;AACZ,CAAC"}
package/dist/tasks.d.ts CHANGED
@@ -32,6 +32,7 @@ declare class TaskManager {
32
32
  created: number;
33
33
  skipped: number;
34
34
  }>;
35
+ private appendTaskTombstone;
35
36
  /** Write a single task to SQLite + JSONL audit */
36
37
  private writeTaskToDb;
37
38
  /** Legacy bulk persist — now just writes all tasks from DB to JSONL */
@@ -73,6 +74,13 @@ declare class TaskManager {
73
74
  * fields without triggering full validation or history events.
74
75
  */
75
76
  patchTaskMetadata(id: string, metadataUpdates: Record<string, unknown>): boolean;
77
+ getTaskDeletionTombstone(inputId: string): {
78
+ taskId: string;
79
+ deletedAt: number;
80
+ deletedBy: string;
81
+ previousStatus: string;
82
+ title: string;
83
+ } | null;
76
84
  getTaskHistory(id: string): TaskHistoryEvent[];
77
85
  getTaskComments(id: string, options?: {
78
86
  includeSuppressed?: boolean;
@@ -105,7 +113,7 @@ declare class TaskManager {
105
113
  private parseLaneTransition;
106
114
  private applyLaneStateLock;
107
115
  updateTask(id: string, updates: Partial<Omit<Task, 'id' | 'createdAt' | 'createdBy'>>): Promise<Task | undefined>;
108
- deleteTask(id: string): Promise<boolean>;
116
+ deleteTask(id: string, actor?: string): Promise<boolean>;
109
117
  subscribe(callback: (task: Task, action: 'created' | 'updated' | 'deleted') => void): () => boolean;
110
118
  private notifySubscribers;
111
119
  private checkUnblockedTasks;
@@ -1 +1 @@
1
- {"version":3,"file":"tasks.d.ts","sourceRoot":"","sources":["../src/tasks.ts"],"names":[],"mappings":"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"}
1
+ {"version":3,"file":"tasks.d.ts","sourceRoot":"","sources":["../src/tasks.ts"],"names":[],"mappings":"AAGA;;GAEG;AACH,OAAO,KAAK,EAAE,IAAI,EAAE,aAAa,EAAE,qBAAqB,EAAE,gBAAgB,EAAE,WAAW,EAAE,MAAM,YAAY,CAAA;AA8P3G,cAAM,WAAW;IAEf,OAAO,CAAC,WAAW,CAA6E;IAChG,OAAO,CAAC,cAAc,CAAmC;IAEzD,OAAO,CAAC,WAAW,CAAQ;IAC3B,OAAO,CAAC,oBAAoB,CAAQ;IACpC,OAAO,CAAC,eAAe,CAAgB;IACvC,OAAO,CAAC,gBAAgB,CAA2D;IAEnF,OAAO,CAAC,uBAAuB;IAW/B,OAAO,CAAC,sBAAsB;;YAwDhB,SAAS;IAmDvB,OAAO,CAAC,sBAAsB;YAOhB,kBAAkB;IAmEhC,6EAA6E;YAC/D,mBAAmB;IAYjC,8EAA8E;YAChE,oBAAoB;YAYpB,iBAAiB;YA+BjB,sBAAsB;YAkBtB,iBAAiB;YA6DjB,qBAAqB;IA+CnC,OAAO,CAAC,gBAAgB;IAiCxB,OAAO,CAAC,kBAAkB;IAY1B,OAAO,CAAC,0BAA0B;IAW5B,4BAA4B,CAAC,GAAG,SAAa,EAAE,OAAO,CAAC,EAAE;QAAE,KAAK,CAAC,EAAE,OAAO,CAAA;KAAE,GAAG,OAAO,CAAC;QAAE,OAAO,EAAE,MAAM,CAAC;QAAC,OAAO,EAAE,MAAM,CAAA;KAAE,CAAC;YA+DpH,mBAAmB;IAcjC,kDAAkD;IAClD,OAAO,CAAC,aAAa;IA2ErB,uEAAuE;YACzD,YAAY;YAWZ,eAAe;YAUf,qBAAqB;IAU7B,UAAU,CAAC,IAAI,EAAE,IAAI,CAAC,IAAI,EAAE,IAAI,GAAG,WAAW,GAAG,WAAW,CAAC,GAAG,OAAO,CAAC,IAAI,CAAC;IAiE7E,mBAAmB,CAAC,IAAI,EAAE;QAC9B,KAAK,EAAE,MAAM,CAAA;QACb,WAAW,CAAC,EAAE,MAAM,CAAA;QACpB,QAAQ,CAAC,EAAE,MAAM,CAAA;QACjB,QAAQ,CAAC,EAAE,MAAM,CAAA;QACjB,aAAa,CAAC,EAAE,MAAM,EAAE,CAAA;QACxB,SAAS,EAAE,MAAM,CAAA;QACjB,QAAQ,CAAC,EAAE,IAAI,CAAC,UAAU,CAAC,CAAA;QAC3B,UAAU,CAAC,EAAE,MAAM,EAAE,CAAA;QACrB,OAAO,CAAC,EAAE,MAAM,CAAA;QAChB,IAAI,CAAC,EAAE,MAAM,EAAE,CAAA;QACf,QAAQ,CAAC,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,CAAA;QAClC,QAAQ,EAAE,qBAAqB,CAAA;QAC/B,OAAO,CAAC,EAAE,OAAO,CAAA;QACjB,MAAM,CAAC,EAAE,aAAa,CAAC,QAAQ,CAAC,CAAA;KACjC,GAAG,OAAO,CAAC,aAAa,CAAC;IAsC1B,kBAAkB,CAAC,OAAO,CAAC,EAAE;QAAE,OAAO,CAAC,EAAE,OAAO,CAAA;KAAE,GAAG,aAAa,EAAE;IAQ9D,mBAAmB,CACvB,EAAE,EAAE,MAAM,EACV,OAAO,EAAE,OAAO,CAAC,IAAI,CAAC,aAAa,EAAE,SAAS,GAAG,UAAU,CAAC,CAAC,GAC5D,OAAO,CAAC,aAAa,GAAG,SAAS,CAAC;IAuB/B,mBAAmB,CAAC,EAAE,EAAE,MAAM,GAAG,OAAO,CAAC,OAAO,CAAC;IAOvD,OAAO,CAAC,EAAE,EAAE,MAAM,GAAG,IAAI,GAAG,SAAS;IAIrC,aAAa,CAAC,OAAO,EAAE,MAAM,GAAG;QAC9B,IAAI,CAAC,EAAE,IAAI,CAAA;QACX,UAAU,CAAC,EAAE,MAAM,CAAA;QACnB,SAAS,EAAE,OAAO,GAAG,QAAQ,GAAG,WAAW,GAAG,WAAW,CAAA;QACzD,WAAW,EAAE,MAAM,EAAE,CAAA;KACtB;IAwCD;;;;OAIG;IACH,iBAAiB,CAAC,EAAE,EAAE,MAAM,EAAE,eAAe,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,GAAG,OAAO;IAShF,wBAAwB,CAAC,OAAO,EAAE,MAAM,GAAG;QAAE,MAAM,EAAE,MAAM,CAAC;QAAC,SAAS,EAAE,MAAM,CAAC;QAAC,SAAS,EAAE,MAAM,CAAC;QAAC,cAAc,EAAE,MAAM,CAAC;QAAC,KAAK,EAAE,MAAM,CAAA;KAAE,GAAG,IAAI;IA4CjJ,cAAc,CAAC,EAAE,EAAE,MAAM,GAAG,gBAAgB,EAAE;IAe9C,eAAe,CAAC,EAAE,EAAE,MAAM,EAAE,OAAO,CAAC,EAAE;QAAE,iBAAiB,CAAC,EAAE,OAAO,CAAA;KAAE,GAAG,WAAW,EAAE;IAiCrF,mBAAmB,CAAC,EAAE,EAAE,MAAM,GAAG,MAAM;IAMjC,cAAc,CAClB,MAAM,EAAE,MAAM,EACd,MAAM,EAAE,MAAM,EACd,OAAO,EAAE,MAAM,EACf,OAAO,CAAC,EAAE;QAAE,QAAQ,CAAC,EAAE,MAAM,GAAG,IAAI,CAAC;QAAC,UAAU,CAAC,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,GAAG,IAAI,CAAA;KAAE,GAClF,OAAO,CAAC,WAAW,CAAC;IAyEvB,yEAAyE;IACzE,OAAO,CAAC,0BAA0B,CAAI;IACtC,OAAO,CAAC,MAAM,CAAC,QAAQ,CAAC,qBAAqB,CAAS;IAEtD,SAAS,CAAC,OAAO,CAAC,EAAE;QAClB,0EAA0E;QAC1E,MAAM,CAAC,EAAE,IAAI,CAAC,QAAQ,CAAC,GAAG,IAAI,CAAC,QAAQ,CAAC,EAAE,CAAA;QAC1C,gFAAgF;QAChF,QAAQ,CAAC,EAAE,MAAM,CAAA;QACjB,4CAA4C;QAC5C,UAAU,CAAC,EAAE,MAAM,EAAE,CAAA;QACrB,UAAU,CAAC,EAAE,MAAM,CAAA;QACnB,SAAS,CAAC,EAAE,MAAM,CAAA;QAClB,MAAM,CAAC,EAAE,MAAM,CAAA;QACf,QAAQ,CAAC,EAAE,IAAI,CAAC,UAAU,CAAC,CAAA;QAC3B,IAAI,CAAC,EAAE,MAAM,EAAE,CAAA;QACf,cAAc,CAAC,EAAE,OAAO,CAAA;QACxB,WAAW,CAAC,EAAE,OAAO,CAAA;KACtB,GAAG,IAAI,EAAE;IAwFV,WAAW,CAAC,KAAK,EAAE,MAAM,GAAG,IAAI,EAAE;IAclC,OAAO,CAAC,mBAAmB;IAiB3B,OAAO,CAAC,mBAAmB;IAM3B,OAAO,CAAC,kBAAkB;IAuIpB,UAAU,CAAC,EAAE,EAAE,MAAM,EAAE,OAAO,EAAE,OAAO,CAAC,IAAI,CAAC,IAAI,EAAE,IAAI,GAAG,WAAW,GAAG,WAAW,CAAC,CAAC,GAAG,OAAO,CAAC,IAAI,GAAG,SAAS,CAAC;IA+FjH,UAAU,CAAC,EAAE,EAAE,MAAM,EAAE,KAAK,SAAW,GAAG,OAAO,CAAC,OAAO,CAAC;IA0DhE,SAAS,CAAC,QAAQ,EAAE,CAAC,IAAI,EAAE,IAAI,EAAE,MAAM,EAAE,SAAS,GAAG,SAAS,GAAG,SAAS,KAAK,IAAI;IAKnF,OAAO,CAAC,iBAAiB;IAUzB,OAAO,CAAC,mBAAmB;IAyC3B,WAAW,CAAC,KAAK,CAAC,EAAE,MAAM,EAAE,IAAI,CAAC,EAAE;QAAE,WAAW,CAAC,EAAE,OAAO,CAAA;KAAE,GAAG,IAAI,GAAG,SAAS;IA4E/E,2BAA2B;;;;;;;;;;;;;;;;;IAsC3B,QAAQ,CAAC,OAAO,CAAC,EAAE;QAAE,WAAW,CAAC,EAAE,OAAO,CAAA;KAAE;;;;CAiB7C;AAED,eAAO,MAAM,WAAW,aAAoB,CAAA"}
package/dist/tasks.js CHANGED
@@ -25,8 +25,14 @@ function importTasks(db, records) {
25
25
  tags, metadata, team_id, comment_count, due_at, scheduled_for
26
26
  ) VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?)
27
27
  `);
28
+ const del = db.prepare('DELETE FROM tasks WHERE id = ?');
28
29
  const insertMany = db.transaction((tasks) => {
29
30
  for (const record of tasks) {
31
+ const tombstone = record;
32
+ if (tombstone?.deleted === true && typeof tombstone.id === 'string' && tombstone.id.trim()) {
33
+ del.run(tombstone.id);
34
+ continue;
35
+ }
30
36
  const task = record;
31
37
  insert.run(task.id, task.title, task.description ?? null, task.status, task.assignee ?? null, task.reviewer ?? null, safeJsonStringify(task.done_criteria), task.createdBy, task.createdAt, task.updatedAt, task.priority ?? null, safeJsonStringify(task.blocked_by), task.epic_id ?? null, safeJsonStringify(task.tags), safeJsonStringify(task.metadata), task.teamId ?? null, 0, // comment_count will be recalculated when comments are imported
32
38
  task.dueAt ?? null, task.scheduledFor ?? null);
@@ -336,12 +342,18 @@ class TaskManager {
336
342
  VALUES (?, ?, ?, ?, ?, ?)
337
343
  `);
338
344
  insert.run(event.id, event.taskId, event.type, event.actor, event.timestamp, safeJsonStringify(event.data));
345
+ }
346
+ catch (err) {
347
+ console.error('[Tasks] Failed to append task history (DB):', err);
348
+ throw err;
349
+ }
350
+ try {
339
351
  // Append to JSONL (audit log)
340
352
  await fs.mkdir(DATA_DIR, { recursive: true });
341
353
  await fs.appendFile(TASK_HISTORY_FILE, `${JSON.stringify(event)}\n`, 'utf-8');
342
354
  }
343
355
  catch (err) {
344
- console.error('[Tasks] Failed to append task history:', err);
356
+ console.error('[Tasks] Failed to append task history (JSONL):', err);
345
357
  }
346
358
  }
347
359
  async recordTaskHistoryEvent(taskId, type, actor, data) {
@@ -356,39 +368,48 @@ class TaskManager {
356
368
  await this.appendTaskHistory(event);
357
369
  }
358
370
  async appendTaskComment(comment) {
371
+ const db = getDb();
372
+ // DB writes must be atomic; otherwise callers may observe a "phantom" comment ID
373
+ // that never persisted (breaking review_handoff.comment_id).
374
+ const insert = db.prepare(`
375
+ INSERT INTO task_comments (
376
+ id, task_id, author, content, timestamp,
377
+ category, suppressed, suppressed_reason, suppressed_rule
378
+ ) VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?)
379
+ `);
380
+ const updateTask = db.prepare(`
381
+ UPDATE tasks
382
+ SET
383
+ comment_count = (SELECT COUNT(*) FROM task_comments WHERE task_id = ?),
384
+ updated_at = ?
385
+ WHERE id = ?
386
+ `);
387
+ const touchUpdatedAt = db.prepare(`
388
+ UPDATE tasks
389
+ SET updated_at = MAX(updated_at, ?)
390
+ WHERE id = ?
391
+ `);
392
+ try {
393
+ const tx = db.transaction(() => {
394
+ insert.run(comment.id, comment.taskId, comment.author, comment.content, comment.timestamp, comment.category ?? null, comment.suppressed ? 1 : 0, comment.suppressedReason ?? null, comment.suppressedRule ?? null);
395
+ // Comments are material activity and should advance updated_at to avoid autonomy/heartbeat false positives.
396
+ updateTask.run(comment.taskId, comment.timestamp, comment.taskId);
397
+ touchUpdatedAt.run(comment.timestamp, comment.taskId);
398
+ });
399
+ tx();
400
+ }
401
+ catch (err) {
402
+ // Critical: propagate failures so API callers don't record unresolvable comment IDs.
403
+ console.error('[Tasks] Failed to append task comment (DB):', err);
404
+ throw err;
405
+ }
406
+ // Audit log is best-effort; DB is the source of truth.
359
407
  try {
360
- const db = getDb();
361
- // Write to SQLite (primary)
362
- const insert = db.prepare(`
363
- INSERT INTO task_comments (
364
- id, task_id, author, content, timestamp,
365
- category, suppressed, suppressed_reason, suppressed_rule
366
- ) VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?)
367
- `);
368
- insert.run(comment.id, comment.taskId, comment.author, comment.content, comment.timestamp, comment.category ?? null, comment.suppressed ? 1 : 0, comment.suppressedReason ?? null, comment.suppressedRule ?? null);
369
- // Update comment count + updated_at for the task.
370
- // Comments are material activity and should advance updated_at to avoid autonomy/heartbeat false positives.
371
- const updateTask = db.prepare(`
372
- UPDATE tasks
373
- SET
374
- comment_count = (SELECT COUNT(*) FROM task_comments WHERE task_id = ?),
375
- updated_at = ?
376
- WHERE id = ?
377
- `);
378
- updateTask.run(comment.taskId, comment.timestamp, comment.taskId);
379
- // Touch task updated_at so comment activity counts as task activity (autonomy, SLA, sorting)
380
- const touchUpdatedAt = db.prepare(`
381
- UPDATE tasks
382
- SET updated_at = MAX(updated_at, ?)
383
- WHERE id = ?
384
- `);
385
- touchUpdatedAt.run(comment.timestamp, comment.taskId);
386
- // Append to JSONL (audit log)
387
408
  await fs.mkdir(DATA_DIR, { recursive: true });
388
409
  await fs.appendFile(TASK_COMMENTS_FILE, `${JSON.stringify(comment)}\n`, 'utf-8');
389
410
  }
390
411
  catch (err) {
391
- console.error('[Tasks] Failed to append task comment:', err);
412
+ console.error('[Tasks] Failed to append task comment (JSONL):', err);
392
413
  }
393
414
  }
394
415
  async persistRecurringTasks() {
@@ -526,18 +547,48 @@ class TaskManager {
526
547
  }
527
548
  return { created, skipped };
528
549
  }
550
+ async appendTaskTombstone(task, deletedBy, deletedAt) {
551
+ try {
552
+ await fs.mkdir(DATA_DIR, { recursive: true });
553
+ await fs.appendFile(TASKS_FILE, `${JSON.stringify({
554
+ id: task.id,
555
+ deleted: true,
556
+ deletedAt,
557
+ deletedBy,
558
+ })}\n`, 'utf-8');
559
+ }
560
+ catch (err) {
561
+ console.error('[Tasks] Failed to append task tombstone:', err);
562
+ }
563
+ }
529
564
  /** Write a single task to SQLite + JSONL audit */
530
565
  writeTaskToDb(task) {
531
566
  try {
532
567
  const db = getDb();
533
568
  const commentCount = this.getTaskCommentCount(task.id);
534
- db.prepare(`
535
- INSERT OR REPLACE INTO tasks (
536
- id, title, description, status, assignee, reviewer, done_criteria,
537
- created_by, created_at, updated_at, priority, blocked_by, epic_id,
538
- tags, metadata, team_id, comment_count, due_at, scheduled_for
539
- ) VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?)
540
- `).run(task.id, task.title, task.description ?? null, task.status, task.assignee ?? null, task.reviewer ?? null, safeJsonStringify(task.done_criteria), task.createdBy, task.createdAt, task.updatedAt, task.priority ?? null, safeJsonStringify(task.blocked_by), task.epic_id ?? null, safeJsonStringify(task.tags), safeJsonStringify(task.metadata), task.teamId ?? null, commentCount, task.dueAt ?? null, task.scheduledFor ?? null);
569
+ // Use UPDATE for existing tasks to avoid INSERT OR REPLACE which triggers
570
+ // ON DELETE CASCADE on foreign keys (task_comments, task_history), wiping
571
+ // all comments/history for the task. Only INSERT for genuinely new tasks.
572
+ const existing = db.prepare('SELECT 1 FROM tasks WHERE id = ?').get(task.id);
573
+ if (existing) {
574
+ db.prepare(`
575
+ UPDATE tasks SET
576
+ title = ?, description = ?, status = ?, assignee = ?, reviewer = ?,
577
+ done_criteria = ?, created_by = ?, created_at = ?, updated_at = ?,
578
+ priority = ?, blocked_by = ?, epic_id = ?, tags = ?, metadata = ?,
579
+ team_id = ?, comment_count = ?, due_at = ?, scheduled_for = ?
580
+ WHERE id = ?
581
+ `).run(task.title, task.description ?? null, task.status, task.assignee ?? null, task.reviewer ?? null, safeJsonStringify(task.done_criteria), task.createdBy, task.createdAt, task.updatedAt, task.priority ?? null, safeJsonStringify(task.blocked_by), task.epic_id ?? null, safeJsonStringify(task.tags), safeJsonStringify(task.metadata), task.teamId ?? null, commentCount, task.dueAt ?? null, task.scheduledFor ?? null, task.id);
582
+ }
583
+ else {
584
+ db.prepare(`
585
+ INSERT INTO tasks (
586
+ id, title, description, status, assignee, reviewer, done_criteria,
587
+ created_by, created_at, updated_at, priority, blocked_by, epic_id,
588
+ tags, metadata, team_id, comment_count, due_at, scheduled_for
589
+ ) VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?)
590
+ `).run(task.id, task.title, task.description ?? null, task.status, task.assignee ?? null, task.reviewer ?? null, safeJsonStringify(task.done_criteria), task.createdBy, task.createdAt, task.updatedAt, task.priority ?? null, safeJsonStringify(task.blocked_by), task.epic_id ?? null, safeJsonStringify(task.tags), safeJsonStringify(task.metadata), task.teamId ?? null, commentCount, task.dueAt ?? null, task.scheduledFor ?? null);
591
+ }
541
592
  }
542
593
  catch (err) {
543
594
  console.error(`[Tasks] Failed to write task ${task.id} to SQLite:`, err);
@@ -750,6 +801,45 @@ class TaskManager {
750
801
  this.writeTaskToDb(updated);
751
802
  return true;
752
803
  }
804
+ getTaskDeletionTombstone(inputId) {
805
+ const db = getDb();
806
+ const raw = String(inputId || '').trim();
807
+ if (!raw)
808
+ return null;
809
+ // Try exact match first, then prefix match against task_history task_ids.
810
+ let taskId = raw;
811
+ const exactRow = db.prepare(`SELECT task_id, data FROM task_history WHERE task_id = ? AND type = 'deleted' ORDER BY timestamp DESC LIMIT 1`).get(raw);
812
+ if (!exactRow) {
813
+ // Prefix match: find task_ids in history that start with the input.
814
+ const prefixRows = db.prepare(`SELECT task_id FROM task_history WHERE task_id LIKE ? AND type = 'deleted' LIMIT 2`).all(raw + '%');
815
+ if (prefixRows.length !== 1)
816
+ return null;
817
+ taskId = prefixRows[0].task_id;
818
+ const prefixRow = db.prepare(`SELECT data FROM task_history WHERE task_id = ? AND type = 'deleted' ORDER BY timestamp DESC LIMIT 1`).get(taskId);
819
+ if (!prefixRow)
820
+ return null;
821
+ const prefixData = safeJsonParse(prefixRow.data);
822
+ if (!prefixData)
823
+ return null;
824
+ return {
825
+ taskId,
826
+ deletedAt: typeof prefixData.deletedAt === 'number' ? prefixData.deletedAt : 0,
827
+ deletedBy: typeof prefixData.deletedBy === 'string' ? prefixData.deletedBy : 'unknown',
828
+ previousStatus: typeof prefixData.previousStatus === 'string' ? prefixData.previousStatus : 'unknown',
829
+ title: typeof prefixData.title === 'string' ? prefixData.title : '',
830
+ };
831
+ }
832
+ const data = safeJsonParse(exactRow.data);
833
+ if (!data)
834
+ return null;
835
+ return {
836
+ taskId,
837
+ deletedAt: typeof data.deletedAt === 'number' ? data.deletedAt : 0,
838
+ deletedBy: typeof data.deletedBy === 'string' ? data.deletedBy : 'unknown',
839
+ previousStatus: typeof data.previousStatus === 'string' ? data.previousStatus : 'unknown',
840
+ title: typeof data.title === 'string' ? data.title : '',
841
+ };
842
+ }
753
843
  getTaskHistory(id) {
754
844
  const db = getDb();
755
845
  const rows = db.prepare('SELECT * FROM task_history WHERE task_id = ? ORDER BY timestamp ASC').all(id);
@@ -986,6 +1076,37 @@ class TaskManager {
986
1076
  return transition;
987
1077
  };
988
1078
  let transitionEvent;
1079
+ // ── Bounce gate: validating → doing ────────────────────────────────────
1080
+ // A task bouncing from validating back to doing is a signal of rework.
1081
+ // Track bounce_count in metadata. On the 3rd bounce (bounce_count >= 2),
1082
+ // require a documented reason before allowing the regression.
1083
+ if (task.status === 'validating' && nextStatus === 'doing') {
1084
+ const meta = (task.metadata || {});
1085
+ const currentBounce = typeof meta.bounce_count === 'number' ? meta.bounce_count : 0;
1086
+ const newBounceCount = currentBounce + 1;
1087
+ if (currentBounce >= 2) {
1088
+ // 3rd+ bounce: require documented reason
1089
+ if (!transition || typeof transition !== 'object') {
1090
+ throw new Error(`Bounce gate: this task has bounced ${currentBounce} times. ` +
1091
+ `Provide metadata.transition = { type: "bounce_back", reason: "..." } explaining the rework.`);
1092
+ }
1093
+ const bounceReason = transition.reason;
1094
+ if (typeof bounceReason !== 'string' || bounceReason.trim().length === 0) {
1095
+ throw new Error(`Bounce gate: metadata.transition.reason is required when re-opening a task that has bounced ${currentBounce} times.`);
1096
+ }
1097
+ }
1098
+ // Merge bounce_count into metadata, preserving existing task metadata
1099
+ // (task.metadata has eta etc; updates.metadata may have transition or be empty)
1100
+ const existingMeta = {
1101
+ ...(task.metadata || {}),
1102
+ ...(updates.metadata || {}),
1103
+ };
1104
+ updates.metadata = {
1105
+ ...existingMeta,
1106
+ bounce_count: newBounceCount,
1107
+ last_bounce_at: Date.now(),
1108
+ };
1109
+ }
989
1110
  if (task.status === 'doing' && nextStatus === 'blocked') {
990
1111
  const parsed = requireTransition('pause', ['reason'], 'doing->blocked transition');
991
1112
  transitionEvent = {
@@ -1125,20 +1246,51 @@ class TaskManager {
1125
1246
  }
1126
1247
  return updated;
1127
1248
  }
1128
- async deleteTask(id) {
1249
+ async deleteTask(id, actor = 'system') {
1129
1250
  const task = queryTask(id);
1130
1251
  if (!task)
1131
1252
  return false;
1132
- // Delete from SQLite
1253
+ const deletedAt = Date.now();
1254
+ const event = {
1255
+ id: `thevt-${deletedAt}-${Math.random().toString(36).substr(2, 9)}`,
1256
+ taskId: id,
1257
+ type: 'deleted',
1258
+ actor,
1259
+ timestamp: deletedAt,
1260
+ data: {
1261
+ deletedAt,
1262
+ deletedBy: actor,
1263
+ previousStatus: task.status,
1264
+ title: task.title,
1265
+ },
1266
+ };
1267
+ // Make the live-row delete visible immediately, even if a caller forgets to await deleteTask().
1268
+ // Keep history/comments as audit.
1133
1269
  try {
1134
1270
  const db = getDb();
1135
- db.prepare('DELETE FROM tasks WHERE id = ?').run(id);
1136
- db.prepare('DELETE FROM task_comments WHERE task_id = ?').run(id);
1137
- db.prepare('DELETE FROM task_history WHERE task_id = ?').run(id);
1271
+ const insertHistory = db.prepare(`
1272
+ INSERT INTO task_history (id, task_id, type, actor, timestamp, data)
1273
+ VALUES (?, ?, ?, ?, ?, ?)
1274
+ `);
1275
+ const delTask = db.prepare('DELETE FROM tasks WHERE id = ?');
1276
+ const tx = db.transaction(() => {
1277
+ insertHistory.run(event.id, event.taskId, event.type, event.actor, event.timestamp, safeJsonStringify(event.data));
1278
+ delTask.run(id);
1279
+ });
1280
+ tx();
1138
1281
  }
1139
1282
  catch (err) {
1140
1283
  console.error(`[Tasks] SQLite delete failed for ${id}:`, err);
1284
+ throw err;
1285
+ }
1286
+ try {
1287
+ await fs.mkdir(DATA_DIR, { recursive: true });
1288
+ await fs.appendFile(TASK_HISTORY_FILE, `${JSON.stringify(event)}\n`, 'utf-8');
1289
+ }
1290
+ catch (err) {
1291
+ console.error('[Tasks] Failed to append delete history (JSONL):', err);
1141
1292
  }
1293
+ await this.appendTaskTombstone(task, actor, deletedAt);
1142
1294
  await this.syncTaskDeleteToCloud(id);
1143
1295
  this.notifySubscribers(task, 'deleted');
1144
1296
  return true;