toolroll 0.9.84 → 0.9.86

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 (132) hide show
  1. package/CHANGELOG.md +66 -0
  2. package/README.md +94 -1193
  3. package/dist/agent-fence.d.ts +9 -5
  4. package/dist/agent-fence.js +6 -5
  5. package/dist/agent-onboard.d.ts +2 -0
  6. package/dist/agent-onboard.js +3 -0
  7. package/dist/assignment.js +4 -3
  8. package/dist/browser/workspace.css +1 -1
  9. package/dist/browser/workspace.js +33 -31
  10. package/dist/browser-workspace.d.ts +45 -0
  11. package/dist/chat-actions.d.ts +11 -1
  12. package/dist/chat-actions.js +53 -1
  13. package/dist/chat-channel.d.ts +10 -1
  14. package/dist/chat-channel.js +36 -5
  15. package/dist/chat-decide.d.ts +16 -4
  16. package/dist/chat-decide.js +239 -31
  17. package/dist/chat-messages.d.ts +5 -0
  18. package/dist/chat-messages.js +1 -1
  19. package/dist/chat-quiet.js +6 -4
  20. package/dist/chat-review.d.ts +4 -0
  21. package/dist/chat-review.js +13 -4
  22. package/dist/chat-terms.d.ts +9 -0
  23. package/dist/chat-terms.js +46 -0
  24. package/dist/claim.d.ts +3 -0
  25. package/dist/claim.js +4 -1
  26. package/dist/contracts/chat-actions.d.ts +9 -0
  27. package/dist/contracts/chat-actions.js +3 -0
  28. package/dist/contracts/console-api.d.ts +6 -2
  29. package/dist/contracts/console-api.js +6 -2
  30. package/dist/contracts/flow-propose.d.ts +4 -1
  31. package/dist/contracts/flow-propose.js +1 -1
  32. package/dist/contracts/flow.d.ts +41 -0
  33. package/dist/contracts/flow.js +4 -2
  34. package/dist/contracts/lead-tools.d.ts +51 -0
  35. package/dist/contracts/lead-tools.js +5 -0
  36. package/dist/contracts/store-json.d.ts +2 -0
  37. package/dist/contracts/trigger-payloads.d.ts +20 -0
  38. package/dist/contracts/trigger-payloads.js +4 -0
  39. package/dist/contracts/youiyoux-request.d.ts +248 -0
  40. package/dist/contracts/youiyoux-request.js +152 -0
  41. package/dist/coordinator.js +1 -0
  42. package/dist/deploy-gate.js +4 -0
  43. package/dist/dispatch.d.ts +3 -0
  44. package/dist/dispatch.js +4 -1
  45. package/dist/flow-browser.d.ts +103 -0
  46. package/dist/flow-browser.js +204 -0
  47. package/dist/flow-cadence.d.ts +8 -2
  48. package/dist/flow-cadence.js +16 -3
  49. package/dist/flow-card-task.d.ts +9 -0
  50. package/dist/flow-card-task.js +15 -6
  51. package/dist/flow-code.d.ts +5 -1
  52. package/dist/flow-code.js +4 -1
  53. package/dist/flow-engine.js +16 -0
  54. package/dist/flow-live.js +9 -1
  55. package/dist/flow-pull-request.js +15 -7
  56. package/dist/flow-share.js +3 -1
  57. package/dist/flow-steps.d.ts +9 -1
  58. package/dist/flow-steps.js +55 -5
  59. package/dist/flow-triggers.js +55 -0
  60. package/dist/flows-cli.d.ts +19 -0
  61. package/dist/flows-cli.js +47 -2
  62. package/dist/flows-ui.js +11 -2
  63. package/dist/flows.js +5 -2
  64. package/dist/integrations-ui.js +9 -4
  65. package/dist/integrations.d.ts +9 -1
  66. package/dist/integrations.js +43 -2
  67. package/dist/landed-cleanup.d.ts +4 -0
  68. package/dist/landed-cleanup.js +3 -2
  69. package/dist/lead-capabilities.js +2 -1
  70. package/dist/lead-cli.js +3 -1
  71. package/dist/lead-tools.d.ts +2 -0
  72. package/dist/lead-tools.js +65 -10
  73. package/dist/lead-voice.js +16 -5
  74. package/dist/lead.d.ts +2 -0
  75. package/dist/lead.js +3 -3
  76. package/dist/operate.d.ts +2 -3
  77. package/dist/operate.js +226 -21
  78. package/dist/project-activity.d.ts +20 -0
  79. package/dist/project-activity.js +23 -0
  80. package/dist/project-cli.d.ts +1 -0
  81. package/dist/project-cli.js +36 -19
  82. package/dist/project-delete.js +7 -3
  83. package/dist/project-knowledge.d.ts +3 -3
  84. package/dist/project-outcomes.d.ts +119 -0
  85. package/dist/project-outcomes.js +79 -0
  86. package/dist/pull-request-flow.js +35 -4
  87. package/dist/result-actions.d.ts +4 -0
  88. package/dist/result-actions.js +41 -9
  89. package/dist/result-follow-ups.js +4 -0
  90. package/dist/result-review.d.ts +9 -0
  91. package/dist/result-review.js +45 -5
  92. package/dist/scope.d.ts +5 -0
  93. package/dist/scope.js +7 -0
  94. package/dist/server/chat.js +4 -2
  95. package/dist/server/console.css +41 -0
  96. package/dist/server/flows.js +15 -0
  97. package/dist/server/pages.js +11 -1
  98. package/dist/server/render-chat.js +3 -2
  99. package/dist/server/render-pages.d.ts +15 -2
  100. package/dist/server/render-pages.js +52 -4
  101. package/dist/server/render-results.d.ts +2 -0
  102. package/dist/server/render-results.js +20 -7
  103. package/dist/server/render-tasks.d.ts +9 -0
  104. package/dist/server/render-tasks.js +32 -11
  105. package/dist/server/route-table.js +12 -0
  106. package/dist/server/settings.js +18 -1
  107. package/dist/server/task-views.js +11 -0
  108. package/dist/server/tasks.d.ts +10 -0
  109. package/dist/server/tasks.js +95 -7
  110. package/dist/settings-proofs.js +1 -0
  111. package/dist/store.d.ts +116 -5
  112. package/dist/store.js +385 -24
  113. package/dist/surface.js +18 -2
  114. package/dist/task-archive.d.ts +10 -0
  115. package/dist/task-archive.js +16 -0
  116. package/dist/task-control.d.ts +1 -1
  117. package/dist/task-source.d.ts +13 -1
  118. package/dist/task-source.js +17 -1
  119. package/dist/telegram-lead.js +7 -5
  120. package/dist/telegram.js +1 -1
  121. package/dist/tidy.d.ts +130 -0
  122. package/dist/tidy.js +503 -0
  123. package/dist/toolroll-update.js +2 -2
  124. package/dist/webhooks.d.ts +6 -0
  125. package/dist/webhooks.js +25 -0
  126. package/dist/work-index.d.ts +28 -0
  127. package/dist/work-index.js +82 -28
  128. package/dist/workspace-ui.d.ts +1 -1
  129. package/dist/workspace-ui.js +3 -2
  130. package/dist/youiyoux-intake.d.ts +234 -0
  131. package/dist/youiyoux-intake.js +908 -0
  132. package/package.json +11 -8
package/dist/surface.js CHANGED
@@ -171,7 +171,7 @@ const GUIDE_ROWS = [
171
171
  notableReasons: ["no-result"] },
172
172
  { invocation: "task list", synopsis: "a page of saved task status", audience: "agent", agentMayInvoke: true, mutation: "none",
173
173
  flags: [jsonFlag, dbFlag, { name: "state", takesValue: true, meaning: "queued|running|done|failed|cancelled" },
174
- { name: "view", takesValue: true, meaning: "all|needs-you|running|completed" },
174
+ { name: "view", takesValue: true, meaning: "all|needs-you|running|completed|archived" },
175
175
  { name: "limit", takesValue: true, meaning: "page size, 1–100; default 40" },
176
176
  { name: "cursor", takesValue: true, meaning: "nextCursor from the previous response" },
177
177
  { name: "repo", takesValue: true, meaning: "filter to one project" }] },
@@ -299,6 +299,18 @@ const GUIDE_ROWS = [
299
299
  operator("task route", "which configured agents plan, build and repair this task; phase and size changes require renewed approval"),
300
300
  operator("task reopen", "resume external work its tracker closed and has been SEEN open again"),
301
301
  operator("task stop", "stop ONE exact live attempt (--run <id>): durable before any process is signalled, answers 'stopping' until its own processes are established gone; work, branch, and evidence preserved; no strike, no retry"),
302
+ { ...operator("task archive", "put tasks away (a person's act): out of every list, count and nudge, history and approvals kept; nothing claims, approves or dispatches them until restored"),
303
+ positionals: [{ name: "id", required: true, meaning: "one or more tasks (any version: the whole family goes)" }],
304
+ flags: [jsonFlag, dbFlag, { name: "reason", takesValue: true, meaning: "why, shown on the task page" }], notableReasons: ["unknown-task", "not-an-approver"] },
305
+ { ...operator("task restore", "bring put-away tasks back exactly as they were (a person's act)"),
306
+ positionals: [{ name: "id", required: true, meaning: "one or more put-away tasks" }], flags: [jsonFlag, dbFlag], notableReasons: ["unknown-task", "not-an-approver"] },
307
+ { ...operator("task tidy", "Put away old tasks: --preview lists what would go and why (read-only), --apply --digest <d> puts away exactly that list (a person's act)"),
308
+ flags: [jsonFlag, dbFlag, { name: "preview", takesValue: false, meaning: "show the list and the digest; changes nothing" },
309
+ { name: "apply", takesValue: false, meaning: "put away exactly the previewed list (needs --digest)" },
310
+ { name: "digest", takesValue: true, meaning: "the digest --preview printed" },
311
+ { name: "days", takesValue: true, meaning: "also finished tasks untouched this many days (1–365, default 14)" },
312
+ { name: "except", takesValue: true, meaning: "comma-separated tasks from the list to leave out" }],
313
+ notableReasons: ["usage", "not-an-approver", "empty"] },
302
314
  operator("run settle", "last resort for a finished run whose process witness can't be proven either way: records your --why in the ledger; refuses while any of the run's processes is alive"),
303
315
  operator("task resume", "resume the exact stopped attempt (--run <id>): refuses until it is quiescent, lifts only that stop's hold, approves nothing — the next pass re-proves the scope and inherits the draft with fresh proof"),
304
316
  // ---- leases (runner surface) ----
@@ -443,11 +455,15 @@ const REMOTE_NO = [
443
455
  "enroll", "revoke",
444
456
  // Flow steps that fetch addresses or run checks and scripts here.
445
457
  "flows import", "flows trigger check", "flows script save",
458
+ // Lets a flow script on this machine start Chromium in its sandbox: a person here, with their own password.
459
+ "flows browser",
446
460
  // Reads this machine owner's own Claude and Codex session files (~/.claude, ~/.codex).
447
461
  "memory propose",
448
462
  ];
449
463
  const REMOTE_STEP_UP = [
450
- "proposals", "decide", "task regate", "task approve", "task accept", "task merge", "run settle", "knowledge apply", "memory apply",
464
+ "proposals", "decide", "task regate", "task approve", "task accept", "task merge", "run settle",
465
+ // Putting tasks away and restoring them: a person's act, in the console or on the server's machine.
466
+ "task archive", "task restore", "task tidy", "knowledge apply", "memory apply",
451
467
  "flows card approve", "flows card send-back", "flows script approve", "flows unattended",
452
468
  "approver list", "approver add", "people list", "people invite", "people projects", "people revoke",
453
469
  // A person's own API tokens: a password step-up on the server's machine, or the console. Never with a token.
@@ -0,0 +1,10 @@
1
+ /** Put-away checks (v145, docs/plans/task-tidy.md), in a leaf module so every door can ask without loading the store. */
2
+ import type { Store } from "./store.js";
3
+ /** Whether this task version is put away. Every door that dispatches, claims or approves asks this (fail closed: a
4
+ * row means no). */
5
+ export declare function refArchived(store: Store, taskRef: number): boolean;
6
+ /** Whether the built-in task with this id is put away. */
7
+ export declare function taskArchived(store: Store, taskId: string): boolean;
8
+ /** Whether a flow card's work is put away: its current task or its first build task (a later stage keeps the first
9
+ * and may clear the current one). Read from the card's own row now, so a put away that committed first holds it. */
10
+ export declare function cardTaskArchived(store: Store, cardId: number): boolean;
@@ -0,0 +1,16 @@
1
+ /** Whether this task version is put away. Every door that dispatches, claims or approves asks this (fail closed: a
2
+ * row means no). */
3
+ export function refArchived(store, taskRef) {
4
+ return store.handle.prepare("SELECT 1 FROM task_archive WHERE task_ref = ?").get(taskRef) !== undefined;
5
+ }
6
+ /** Whether the built-in task with this id is put away. */
7
+ export function taskArchived(store, taskId) {
8
+ return store.handle.prepare(`SELECT 1 FROM task_archive a JOIN task_ref r ON r.id = a.task_ref
9
+ WHERE r.backend = 'built-in' AND r.external_id = ?`).get(taskId) !== undefined;
10
+ }
11
+ /** Whether a flow card's work is put away: its current task or its first build task (a later stage keeps the first
12
+ * and may clear the current one). Read from the card's own row now, so a put away that committed first holds it. */
13
+ export function cardTaskArchived(store, cardId) {
14
+ return store.handle.prepare(`SELECT 1 FROM flow_card c JOIN task_ref r ON r.backend = 'built-in' AND r.external_id IN (c.task, c.primary_task)
15
+ JOIN task_archive a ON a.task_ref = r.id WHERE c.id = ?`).get(cardId) !== undefined;
16
+ }
@@ -102,7 +102,7 @@ export type ResumeOutcome = {
102
102
  gate: DispatchDiagnosis | null;
103
103
  } | {
104
104
  ok: false;
105
- reason: "no-task" | "no-stop" | "wrong-task" | "stopping" | "already-resumed" | "superseded" | "busy" | "review" | "occupied";
105
+ reason: "no-task" | "no-stop" | "wrong-task" | "stopping" | "already-resumed" | "superseded" | "busy" | "review" | "occupied" | "archived";
106
106
  detail: string;
107
107
  };
108
108
  /**
@@ -51,8 +51,20 @@ export type TaskOrigin = {
51
51
  url: string | null;
52
52
  host: string | null;
53
53
  } | null;
54
+ /** A youiyoux change or options request (youiyoux-intake.ts): its id, its kind, and where the person pointed. */
55
+ request?: {
56
+ id: string;
57
+ kind: "change" | "options";
58
+ route: string | null;
59
+ component: string | null;
60
+ at: string | null;
61
+ } | null;
54
62
  };
55
- /** A task's origin, or null when it came from neither a flow nor an outside event. */
63
+ /** A task's origin, or null when it came from neither a flow, an outside event nor a youiyoux request. */
56
64
  export declare function originOf(store: Store, taskId: string, canSee: (repo: string) => boolean): TaskOrigin | null;
65
+ /** A youiyoux request by its kind: "youiyoux change request r-…" or "youiyoux options request r-…". */
66
+ export declare function requestName(request: NonNullable<TaskOrigin["request"]>): string;
67
+ /** Where a youiyoux request pointed, in words: "Button on /flows (src/button.tsx:12)". */
68
+ export declare function requestWords(request: NonNullable<TaskOrigin["request"]>): string;
57
69
  /** The one line an approval shows about an outside event that started its work: context, never a term agreed to. */
58
70
  export declare function originNote(origin: TaskOrigin | null | undefined): string | null;
@@ -71,8 +71,11 @@ export function cardReference(source) {
71
71
  const snapshot = snapshotOf(source ?? null);
72
72
  return snapshot === null ? "" : referenceWords(snapshot);
73
73
  }
74
- /** A task's origin, or null when it came from neither a flow nor an outside event. */
74
+ /** A task's origin, or null when it came from neither a flow, an outside event nor a youiyoux request. */
75
75
  export function originOf(store, taskId, canSee) {
76
+ const request = store.youiyouxOriginOf(taskId);
77
+ if (request !== null)
78
+ return { flow: null, source: null, request: { id: request.request, kind: request.kind, route: request.route, component: request.component, at: request.at } };
76
79
  const kept = store.taskSourceOf(taskId);
77
80
  const flowId = kept?.flow ?? Number(/^flow:(\d+)$/.exec(store.filedViaOf(taskId) ?? "")?.[1] ?? NaN);
78
81
  const snapshot = kept === null ? null : readSnapshot(kept.sourceJson);
@@ -84,8 +87,21 @@ export function originOf(store, taskId, canSee) {
84
87
  source: snapshot === null ? null : { label: snapshot.label, proved: provedWords(snapshot), url: snapshot.external.url, host: hostOf(snapshot.external.url) },
85
88
  };
86
89
  }
90
+ /** A youiyoux request by its kind: "youiyoux change request r-…" or "youiyoux options request r-…". */
91
+ export function requestName(request) {
92
+ return `youiyoux ${request.kind === "options" ? "options" : "change"} request ${request.id}`;
93
+ }
94
+ /** Where a youiyoux request pointed, in words: "Button on /flows (src/button.tsx:12)". */
95
+ export function requestWords(request) {
96
+ const what = [request.component, request.route === null ? null : `on ${request.route}`].filter(one => one !== null).join(" ");
97
+ return `${what === "" ? "" : what}${request.at === null ? "" : `${what === "" ? "" : " "}(${request.at})`}`;
98
+ }
87
99
  /** The one line an approval shows about an outside event that started its work: context, never a term agreed to. */
88
100
  export function originNote(origin) {
101
+ if (origin?.request != null) {
102
+ const where = requestWords(origin.request);
103
+ return `From ${requestName(origin.request)}${where === "" ? "" : `: ${where}`}. Whoever can write the project's folder wrote these words; read them all before you approve.`;
104
+ }
89
105
  if (origin?.source == null)
90
106
  return null;
91
107
  const where = origin.source.host === null ? "" : ` · ${origin.source.host}`;
@@ -1,5 +1,5 @@
1
1
  import { CHAT_ACTIONS, sharedActionAllowsChallenge, sharedActionPayload } from "./chat-actions.js";
2
- import { taskInCeiling, mirrorToTaskChat, couldNotAnswerText, channelRepos as telegramConversationRepos, resolveChannelLead as resolveTelegramLead, NO_PHONE_LINK, NO_TASK_LINK, proposalLink, confirmedLink, proposalPreview, proposalOutcomeText, handoffCardText, confirmedCardText } from "./chat-channel.js";
2
+ import { taskInCeiling, mirrorToTaskChat, couldNotAnswerText, channelRepos as telegramConversationRepos, resolveChannelLead as resolveTelegramLead, NO_PHONE_LINK, NO_TASK_LINK, phoneProposalLink, confirmedLink, phoneProposalPreview, proposalOutcomeText, handoffCardText, confirmedCardText } from "./chat-channel.js";
3
3
  export { channelRepos as telegramConversationRepos, resolveChannelLead as resolveTelegramLead, tooLongText, whichTaskText, replyContextFor, NO_PHONE_LINK, NO_TASK_LINK, proposalLink, confirmedLink, proposalPreview, proposalOutcomeText, handoffCardText, confirmedCardText, CHAT_ACTION_PARITY as TELEGRAM_ACTION_PARITY, parityGaps } from "./chat-channel.js";
4
4
  /**
5
5
  * The paired phone as one more way to use the same assistant.
@@ -207,7 +207,7 @@ export function applyProposalTap(store, binding, token, message, repos, options,
207
207
  return { effects, confirmed: false, ignored: true };
208
208
  }
209
209
  const who = verified.who;
210
- const preview = proposalPreview(store, proposal, actionRepos, "telegram");
210
+ const preview = phoneProposalPreview(store, proposal, actionRepos);
211
211
  const origin = options.phoneOrigin?.() ?? null;
212
212
  if (preview.closed) {
213
213
  store.consumeTelegramProposalActions(proposal.id, now);
@@ -239,7 +239,7 @@ export function applyProposalTap(store, binding, token, message, repos, options,
239
239
  // A forged keyboard on a handoff card: nothing acts; the card is
240
240
  // repainted with its current link (or the honest absence of one).
241
241
  store.consumeTelegramProposalActions(proposal.id, now);
242
- const wanted = proposalLink(store, proposal, actionRepos, "telegram");
242
+ const wanted = phoneProposalLink(store, proposal, actionRepos, "telegram");
243
243
  const link = phoneLinkButton(origin, wanted);
244
244
  ack(link === null ? "this step finishes on the computer" : "open the button below to finish this step");
245
245
  edit(handoffCardText(preview.text, linkNote(origin, wanted)), keyboardWith(null, link));
@@ -545,7 +545,7 @@ async function runTelegramConversation(row, args) {
545
545
  // or went away carries the current truth, never a stored URL.
546
546
  const proposal = part.kind === "card" && part.proposal !== null ? store.getLeadProposal(part.proposal) : null;
547
547
  const origin = proposal === null ? null : options.phoneOrigin?.() ?? null;
548
- const wanted = proposal === null ? null : proposalLink(store, proposal, repos);
548
+ const wanted = proposal === null ? null : phoneProposalLink(store, proposal, repos);
549
549
  const keyboard = keyboardWith(part.keyboard, phoneLinkButton(origin, wanted));
550
550
  // The lead's own words carry their bold anchors and labelled links as entities, never as Markdown characters.
551
551
  const voiced = part.kind === "reply" ? telegramReply(part.text) : null;
@@ -625,7 +625,7 @@ async function runTelegramConversation(row, args) {
625
625
  for (const image of selected)
626
626
  parts.push({ kind: "image", text: image.caption, taskId: image.taskId, run: image.run, artifact: image.artifact, sha256: image.sha256 });
627
627
  for (const proposal of proposals) {
628
- const preview = proposalPreview(store, proposal, repos, "telegram");
628
+ const preview = phoneProposalPreview(store, proposal, repos);
629
629
  const keyboard = preview.buttons ? mintCardTokens(store, binding, proposal.id, now).keyboard : null;
630
630
  parts.push({ kind: "card", text: preview.text, proposal: proposal.id, keyboard });
631
631
  }
@@ -763,11 +763,13 @@ async function runTelegramConversation(row, args) {
763
763
  react: () => transport("setMessageReaction", { chat_id: chatId, message_id: Number(row.messageId), reaction: [{ type: "emoji", emoji: WARM_EMOJI }] }),
764
764
  typing: () => transport("sendChatAction", { chat_id: chatId, action: "typing" }),
765
765
  });
766
+ const replyImage = row.replyTo === null ? undefined : store.telegramMessageBindings(binding, row.replyTo).find(one => one.taskId === row.taskId && one.run === row.sourceRun && one.image !== undefined)?.image;
766
767
  let outcome;
767
768
  try {
768
769
  outcome = await runLeadTurn({
769
770
  store, who, session, thread, config, key: null, message: row.text, requestId: row.request,
770
771
  ...(row.context === null ? {} : { context: row.context }),
772
+ ...(replyImage === undefined ? {} : { replyImage }),
771
773
  ...(options.subscriptionRunner === undefined ? {} : { subscriptionRunner: options.subscriptionRunner }),
772
774
  clock, evidenceRoot: options.evidenceRoot, revalidate, mediaDelivery: "documents", channel: "telegram", onProgress: warm.onProgress,
773
775
  });
package/dist/telegram.js CHANGED
@@ -1852,7 +1852,7 @@ function applyConversation(context, update, effects) {
1852
1852
  if (tasks.length === 1) {
1853
1853
  taskId = tasks[0];
1854
1854
  sourceRun = bindings.find(one => one.run !== null)?.run ?? null;
1855
- context_ = replyContextFor(taskId, sourceRun);
1855
+ context_ = replyContextFor(taskId, sourceRun, bindings.find(one => one.image !== undefined)?.image);
1856
1856
  }
1857
1857
  }
1858
1858
  // No reply target: the task this chat chose to talk about, if any. The
package/dist/tidy.d.ts ADDED
@@ -0,0 +1,130 @@
1
+ import { type Store } from "./store.js";
2
+ import type { WorkSummaryAccess } from "./work-summary.js";
3
+ export type TidyRule = "release" | "routine" | "ci" | "finished";
4
+ /** The preview's groups, in order. "finished" is a person's own choice only; tidy never applies it on its own. */
5
+ export declare const TIDY_GROUPS: readonly {
6
+ rule: TidyRule;
7
+ label: string;
8
+ }[];
9
+ export declare const TIDY_DEFAULT_DAYS = 14;
10
+ /** At most this many tasks are put away automatically per pass. */
11
+ export declare const TIDY_PASS_LIMIT = 25;
12
+ /** One task tidy would put away: its family's current task, every version that goes with it (`members`, task_ref ids),
13
+ * why, and under which rule. */
14
+ export type TidyItem = {
15
+ rule: TidyRule;
16
+ taskId: string;
17
+ title: string;
18
+ repo: string | null;
19
+ reason: string;
20
+ updatedAt: string;
21
+ members: number[];
22
+ };
23
+ /** What the release journals say about one release task (release.ts writes them; read only). */
24
+ export type ReleaseFact = {
25
+ taskId: string;
26
+ version: string;
27
+ product: string;
28
+ candidate: string;
29
+ superseded: boolean;
30
+ done: boolean;
31
+ published: boolean;
32
+ stopped: boolean;
33
+ };
34
+ /** Every release journal's facts, or why they can't be trusted: one unreadable journal leaves the release rule out. */
35
+ export type ReleaseFacts = {
36
+ facts: ReleaseFact[];
37
+ problem: string | null;
38
+ };
39
+ /** Reads `releases/*.json` under the plane's state folder (the database's folder), written only by `toolroll release`.
40
+ * A symlink, a non-file, an oversized or unreadable journal makes the whole answer untrusted (`problem`), so the
41
+ * release rule then puts nothing away: a journal it can't read might be the one that says a release is in flight. */
42
+ export declare function readReleaseFacts(stateDir: string): ReleaseFacts;
43
+ /** The release journals beside this database (its folder is the plane's state folder). An in-memory or relative
44
+ * database has no state folder, so the release rule stays off rather than reading whatever folder it runs in. */
45
+ export declare function releaseFactsFor(databaseFile: string): ReleaseFacts;
46
+ /** -1, 0 or 1: numeric parts first, then a release above any of its pre-releases. */
47
+ export declare function compareVersions(a: string, b: string): number;
48
+ /** The failing runs older "Checks failed" cards of this trigger name (their own GitHub run links) that aren't on record
49
+ * yet, newest first, at most `limit`: the checks trigger reads each from GitHub so tidy can tell where it ran. */
50
+ export declare function unprovenNoticeRuns(store: Store, trigger: number, repo: string, limit?: number): number[];
51
+ /** A completed workflow run on the watched branch, as the checks trigger read it from GitHub (its poll through the
52
+ * person's own gh login, or its signed webhook): kept once per run. Only a push to the watched branch of the watched
53
+ * repository counts (a pull request from a fork whose branch is also called main does not). */
54
+ export declare function recordBranchCheckRun(store: Store, watched: {
55
+ repo: string;
56
+ branch: string;
57
+ }, run: {
58
+ id?: number | undefined;
59
+ name?: string | undefined;
60
+ head_sha?: string | undefined;
61
+ head_branch?: string | undefined;
62
+ conclusion?: string | undefined;
63
+ created_at?: string | undefined;
64
+ event?: string | undefined;
65
+ head_repository?: {
66
+ full_name?: string | undefined;
67
+ } | undefined;
68
+ }, now: Date): boolean;
69
+ /** What tidy would put away now, in group order, oldest first within a group. `days`: also offer finished tasks
70
+ * untouched that long (a person's preview only); null is tidy's own automatic rules. `access`: whose lists. */
71
+ export declare function tidyPreview(store: Store, now: Date, input: {
72
+ releases: ReleaseFacts;
73
+ days: number | null;
74
+ access?: WorkSummaryAccess;
75
+ }): TidyItem[];
76
+ /** The preview's identity: exactly which tasks under which rules, for how many days. A person's confirmation names it. */
77
+ export declare function tidyDigest(items: readonly TidyItem[], days: number | null): string;
78
+ /** The preview in its groups (empty ones left out), with each group's label. */
79
+ export declare function tidyGroups(items: readonly TidyItem[], days: number): {
80
+ rule: TidyRule;
81
+ label: string;
82
+ items: TidyItem[];
83
+ }[];
84
+ /** A day count from a person (1–365), or the default. */
85
+ export declare function tidyDays(raw: string | null | undefined): number | null;
86
+ export type TidyApply = {
87
+ ok: true;
88
+ archived: TidyItem[];
89
+ skipped: {
90
+ taskId: string;
91
+ message: string;
92
+ }[];
93
+ } | {
94
+ ok: false;
95
+ reason: "changed" | "outside" | "empty";
96
+ message: string;
97
+ items: TidyItem[];
98
+ digest: string;
99
+ };
100
+ /** A person puts away exactly what they previewed (or the ticked part of it), in one write transaction that recomputes
101
+ * the preview and refuses when its digest moved or a ticked task isn't in it. */
102
+ export declare function applyTidy(store: Store, now: Date, input: {
103
+ by: string;
104
+ days: number;
105
+ digest: string;
106
+ only: readonly string[] | null;
107
+ releases: ReleaseFacts;
108
+ access?: WorkSummaryAccess;
109
+ }): TidyApply;
110
+ /** Tidy on its own (the worker's reconcile pass): its automatic rules only, at most `limit` tasks, in one write
111
+ * transaction. Idempotent: what is put away is skipped next time, and a restored task is never put away again. */
112
+ export declare function runTidy(store: Store, now: Date, releases: ReleaseFacts, limit?: number, access?: WorkSummaryAccess): TidyItem[];
113
+ /** Whether this person may still put away or restore work in this project, read under the write lock (check and write
114
+ * together): an active approver who can see it. Null when they may; otherwise why not. */
115
+ export declare function approverStandsNow(store: Store, name: string, repo: string | null): string | null;
116
+ /** The repository of a task's family, from its first version (all versions share it). */
117
+ export declare function familyRepoOf(store: Store, refs: readonly number[]): string | null;
118
+ /** "Archived by tidy: <reason>" or "Archived by <person>: <reason>": the one sentence every surface shows. */
119
+ export declare function archivedWords(archived: {
120
+ by: string;
121
+ byKind: "person" | "tidy";
122
+ reason: string;
123
+ }): string;
124
+ /** The release journals beside the store's own database file (none for an in-memory one). */
125
+ export declare function releaseFactsOf(store: Store): ReleaseFacts;
126
+ /** The lists one person sees, for tidy: every project for an unrestricted account, otherwise exactly theirs. The CLI's
127
+ * preview (`--as <name>`) and its apply read the same scope, so the same records give the same digest. */
128
+ export declare function accessOf(store: Store, name: string): WorkSummaryAccess;
129
+ /** Narrows `access` to the caller's own lens (a remote person's project limit): never wider. No lens: unchanged. */
130
+ export declare function withinLens(access: WorkSummaryAccess, lens: readonly string[] | null): WorkSummaryAccess;