toolroll 0.9.14 → 0.9.15

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.
@@ -3,7 +3,9 @@
3
3
  * most one outline act beside it. A result that can't be accepted says why
4
4
  * in one line before them.
5
5
  *
6
- * everything met → Accept
6
+ * everything met → Accept and finish
7
+ * a "You check this one" unanswered → the next check, then Accept without your check
8
+ * a check you marked Not right → Request changes, then Accept anyway
7
9
  * checks queued or running → Checks running (disabled), then Accept without checks
8
10
  * checks didn't run, a check exists → Run checks, then Accept without checks
9
11
  * report and changes disagree,
@@ -11,14 +13,19 @@
11
13
  * never Accept as ink, even with no form here
12
14
  * a Needs you form resolves it → that form (Build again, Confirm it stopped)
13
15
  * saved notes or a failing PR → Revise / Draft a repair task
16
+ * a failed build → Retry (its note starts with what to change), then Run checks when they
17
+ * didn't run; never Accept: a failed build is not a result to accept
18
+ * a failed task's delivered result → Retry, then Accept anyway (in outline, with a reason) where it may be
19
+ * accepted; Run checks stays in its Checks row
14
20
  *
15
- * Accepting means what it always meant: the same forms post to the same
16
- * endpoints. This only decides which one is ink. */
21
+ * Accepting finishes the task in one request (Accept and finish): the person's
22
+ * acceptance, when one is owed, and the completion together. This only decides
23
+ * which act is ink; the page recomputes it as the person answers each check. */
17
24
  /** The words over the reason field an Accept of a refuted result requires. */
18
25
  export declare const ACCEPT_NEEDS_REASON = "Accepting needs a reason";
19
- export type ResultActKind = "accept" | "checks-running" | "run-checks" | "request-changes" | "rebuild" | "confirm-stopped" | "revise" | "draft-repair";
26
+ export type ResultActKind = "retry" | "accept" | "accept-anyway" | "next-check" | "checks-running" | "run-checks" | "request-changes" | "rebuild" | "confirm-stopped" | "revise" | "draft-repair";
20
27
  export type ResultActFacts = {
21
- /** An Accept this page can post (Mark complete, or the person's acceptance); `ready`: every requirement met and checks passed. */
28
+ /** An Accept this page can post (Accept and finish, or the person's acceptance alone); `ready`: every requirement met and checks passed. */
22
29
  accept: {
23
30
  ready: boolean;
24
31
  } | null;
@@ -34,6 +41,16 @@ export type ResultActFacts = {
34
41
  need: "rebuild" | "confirm-stopped" | null;
35
42
  /** Saved notes ready to become a revision, or a failing pull request's repair. */
36
43
  next: "revise" | "draft-repair" | null;
44
+ /** A failed build: `retry` when the task can be retried from here; `acceptAnyway` when it delivered a result that
45
+ * may still be accepted, with a reason. */
46
+ failed?: {
47
+ retry: boolean;
48
+ acceptAnyway?: boolean;
49
+ } | null;
50
+ /** "You check this one" items not answered yet on this page. */
51
+ unanswered?: number;
52
+ /** Items the person marked Not right. */
53
+ notRight?: number;
37
54
  };
38
55
  export type ResultActs = {
39
56
  /** The one ink act, or null when nothing waits on the person. */
@@ -44,3 +61,30 @@ export type ResultActs = {
44
61
  line: string | null;
45
62
  };
46
63
  export declare function resultActsOf(facts: ResultActFacts): ResultActs;
64
+ /** The words a result's Accept wears and the one line before it, given the
65
+ * server's words for everything but the person's own checks (`base`) and how
66
+ * those stand on this page: an unanswered check names itself; a check marked
67
+ * Not right says so; otherwise the base words stand. */
68
+ export type AcceptLabel = "Accept and finish" | "Accept without checks" | "Accept without your check" | "Accept anyway";
69
+ export declare function acceptWithChecksOf(base: {
70
+ label: AcceptLabel;
71
+ ready: boolean;
72
+ why: string | null;
73
+ }, checks: {
74
+ unanswered: readonly string[];
75
+ notRight: readonly string[];
76
+ }): {
77
+ label: AcceptLabel;
78
+ ready: boolean;
79
+ why: string | null;
80
+ };
81
+ /** The Requirements row as the person answers their checks: each Looks right is met, each answer leaves "You check". */
82
+ export declare function requirementsWordsOf(req: {
83
+ met: number;
84
+ total: number;
85
+ yours: number;
86
+ }, looked: number, notRight: number): {
87
+ text: string;
88
+ done: boolean;
89
+ short: boolean;
90
+ };
@@ -3,7 +3,9 @@
3
3
  * most one outline act beside it. A result that can't be accepted says why
4
4
  * in one line before them.
5
5
  *
6
- * everything met → Accept
6
+ * everything met → Accept and finish
7
+ * a "You check this one" unanswered → the next check, then Accept without your check
8
+ * a check you marked Not right → Request changes, then Accept anyway
7
9
  * checks queued or running → Checks running (disabled), then Accept without checks
8
10
  * checks didn't run, a check exists → Run checks, then Accept without checks
9
11
  * report and changes disagree,
@@ -11,14 +13,24 @@
11
13
  * never Accept as ink, even with no form here
12
14
  * a Needs you form resolves it → that form (Build again, Confirm it stopped)
13
15
  * saved notes or a failing PR → Revise / Draft a repair task
16
+ * a failed build → Retry (its note starts with what to change), then Run checks when they
17
+ * didn't run; never Accept: a failed build is not a result to accept
18
+ * a failed task's delivered result → Retry, then Accept anyway (in outline, with a reason) where it may be
19
+ * accepted; Run checks stays in its Checks row
14
20
  *
15
- * Accepting means what it always meant: the same forms post to the same
16
- * endpoints. This only decides which one is ink. */
21
+ * Accepting finishes the task in one request (Accept and finish): the person's
22
+ * acceptance, when one is owed, and the completion together. This only decides
23
+ * which act is ink; the page recomputes it as the person answers each check. */
17
24
  /** The words over the reason field an Accept of a refuted result requires. */
18
25
  export const ACCEPT_NEEDS_REASON = "Accepting needs a reason";
19
26
  export function resultActsOf(facts) {
20
27
  const request = facts.canRequest ? "request-changes" : null;
21
28
  const accept = facts.accept === null ? null : "accept";
29
+ if (facts.failed != null) {
30
+ const checks = facts.runChecks && !facts.checksRunning ? "run-checks" : facts.checksRunning ? "checks-running" : null;
31
+ const anyway = facts.failed.acceptAnyway === true ? "accept-anyway" : null;
32
+ return facts.failed.retry ? { primary: "retry", secondary: anyway ?? checks, line: null } : { primary: checks, secondary: anyway, line: null };
33
+ }
22
34
  if (facts.need !== null)
23
35
  return { primary: facts.need, secondary: request, line: null };
24
36
  if (facts.next !== null)
@@ -26,6 +38,11 @@ export function resultActsOf(facts) {
26
38
  // Can't be accepted yet: Accept is never the ink act, even when the feedback form isn't here.
27
39
  if (facts.blocked !== null)
28
40
  return { primary: request, secondary: accept, line: facts.blocked };
41
+ // The person's own checks come first: the next one is ink until each is answered.
42
+ if (accept !== null && (facts.unanswered ?? 0) > 0)
43
+ return { primary: "next-check", secondary: accept, line: null };
44
+ if (accept !== null && (facts.notRight ?? 0) > 0)
45
+ return { primary: request, secondary: accept, line: null };
29
46
  if (facts.checksRunning)
30
47
  return { primary: "checks-running", secondary: accept ?? request, line: null };
31
48
  if (facts.accept === null)
@@ -36,3 +53,17 @@ export function resultActsOf(facts) {
36
53
  return { primary: "run-checks", secondary: "accept", line: null };
37
54
  return { primary: "accept", secondary: request, line: null };
38
55
  }
56
+ export function acceptWithChecksOf(base, checks) {
57
+ const named = (all) => `“${all[0].replace(/[.!?]+$/, "")}”${all.length > 1 ? ` and ${all.length - 1} more` : ""}`;
58
+ if (checks.unanswered.length > 0)
59
+ return { label: "Accept without your check", ready: false, why: `Not checked yet: ${named(checks.unanswered)}.` };
60
+ if (checks.notRight.length > 0)
61
+ return { label: "Accept anyway", ready: false, why: `You marked ${named(checks.notRight)} not right.` };
62
+ return base;
63
+ }
64
+ /** The Requirements row as the person answers their checks: each Looks right is met, each answer leaves "You check". */
65
+ export function requirementsWordsOf(req, looked, notRight) {
66
+ const met = req.met + looked;
67
+ const yours = Math.max(0, req.yours - looked - notRight);
68
+ return { text: `${met} of ${req.total} met${yours > 0 ? ` · You check ${yours}` : ""}`, done: met === req.total, short: req.total - met - yours > 0 };
69
+ }
@@ -149,9 +149,10 @@ export declare const RESULT_FACT_KEYS: readonly ["run", "head", "base", "head-so
149
149
  * (place, lead, task, user) are not facts and are left out. */
150
150
  export declare function resultFactsFromHtml(html: string): Record<string, string>[];
151
151
  /** Where a result form may send its reader back: the review cockpit's
152
- * own deep link, the chat's result detail, or the run page. Anything else
153
- * lands on the run page. Matched exactly — never a bare open redirect. */
154
- export declare function resultReturnTarget(raw: string | null | undefined, runId: number): string;
152
+ * own deep link, the chat's result detail, the page of a task named in
153
+ * `tasks` (the run's own), or the run page. Anything else lands on the run
154
+ * page. Matched exactly — never a bare open redirect. */
155
+ export declare function resultReturnTarget(raw: string | null | undefined, runId: number, tasks?: readonly string[]): string;
155
156
  /** A form's own request token, minted per render — the same shape the
156
157
  * chat composer uses. A replayed submission carries the same token, so
157
158
  * the server can recognise it and mint nothing twice. */
@@ -149,9 +149,10 @@ export function resultFactsFromHtml(html) {
149
149
  return found;
150
150
  }
151
151
  /** Where a result form may send its reader back: the review cockpit's
152
- * own deep link, the chat's result detail, or the run page. Anything else
153
- * lands on the run page. Matched exactly — never a bare open redirect. */
154
- export function resultReturnTarget(raw, runId) {
152
+ * own deep link, the chat's result detail, the page of a task named in
153
+ * `tasks` (the run's own), or the run page. Anything else lands on the run
154
+ * page. Matched exactly — never a bare open redirect. */
155
+ export function resultReturnTarget(raw, runId, tasks = []) {
155
156
  if (raw !== null && raw !== undefined) {
156
157
  const review = /^\/review\?result=[A-Za-z0-9._~%-]{1,200}(?:&run=([0-9]{1,15}))?(?:&tab=(?:summary|changes|checks))?$/.exec(raw);
157
158
  if (review !== null && (review[1] === undefined || Number(review[1]) === runId))
@@ -159,6 +160,9 @@ export function resultReturnTarget(raw, runId) {
159
160
  const chat = /^\/chat\?task=([A-Za-z0-9._~%-]{1,200})&result=([0-9]{1,15})(?:&conversation=[A-Za-z0-9-]{1,128})?$/.exec(raw);
160
161
  if (chat !== null && Number(chat[2]) === runId)
161
162
  return raw;
163
+ // The run's own task page, which offered Run checks in place; never another task's.
164
+ if (tasks.some(task => raw === `/t/${encodeURIComponent(task)}`))
165
+ return raw;
162
166
  }
163
167
  return `/r/${runId}`;
164
168
  }
package/dist/serve.d.ts CHANGED
@@ -208,6 +208,9 @@ export declare function createDecisionServer(options: ServeOptions): DecisionSer
208
208
  * text, never to a 500. The scheme is a constant, never data.
209
209
  */
210
210
  export declare function editorFileHref(worktree: string, path: string, line?: number | null): string | null;
211
+ /** Earlier versions of a task still in flight, in plain words: queued
212
+ * while they only wait, running once one has started; null when none. */
213
+ export declare function earlierVersionsWords(active: number, running: number): string | null;
211
214
  /** The Agents view every console surface renders (v47): the SAME
212
215
  * projection the CLI prints — who plans, builds, and revises,
213
216
  * whether each was recommended, overridden, or pinned, its reasons, and