@popoverai/dotrequirements 0.35.0 → 0.36.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -0,0 +1,9 @@
1
+ import { type WhereOptions } from "./run-file.js";
2
+ /**
3
+ * `dotreq solution approval complete <decision>`: the agent takes the
4
+ * action on the option the owner chose, exactly as the option carried it,
5
+ * once, and prints what came of it (DECIDE-4.1). Refused, saying why, when
6
+ * she hasn't chosen it, it was taken already, or another agent asked.
7
+ */
8
+ export declare function approvalCompleteCommand(decisionId: string, options: WhereOptions): Promise<void>;
9
+ //# sourceMappingURL=approval.d.ts.map
@@ -0,0 +1,19 @@
1
+ import { api } from "../../convex.js";
2
+ import { solutionClient } from "./client.js";
3
+ import { requireWhere } from "./run-file.js";
4
+ /**
5
+ * `dotreq solution approval complete <decision>`: the agent takes the
6
+ * action on the option the owner chose, exactly as the option carried it,
7
+ * once, and prints what came of it (DECIDE-4.1). Refused, saying why, when
8
+ * she hasn't chosen it, it was taken already, or another agent asked.
9
+ */
10
+ export async function approvalCompleteCommand(decisionId, options) {
11
+ const { solutionId, agent } = requireWhere(options);
12
+ const result = (await solutionClient().mutation(api.solutions.mutations.completeApproval, { solutionId, agent, decisionId }));
13
+ // DECIDE-4.7: refused because the work moved on, kept so the brief stops
14
+ // offering it, and said as any refusal is.
15
+ if ("refused" in result)
16
+ throw new Error(result.refused);
17
+ console.log(result.said);
18
+ }
19
+ //# sourceMappingURL=approval.js.map
@@ -1,9 +1,14 @@
1
1
  import { type WhereOptions } from "./run-file.js";
2
+ export type AskOptions = WhereOptions & {
3
+ /** One action as JSON, `{"tool": ..., "args": ...}`, to ask only whether it may be taken (DECIDE-3.4). */
4
+ approvalRequest?: string;
5
+ };
2
6
  /**
3
- * `dotreq solution ask <request-file>`: the drafter puts a decision to the
4
- * owner, on the outcome it is drafting or the one `--outcome` names. A
5
- * reviewer escalates instead (`dotreq solution review escalate`), and the
6
- * backend refuses its question (VERB-3.5).
7
+ * `dotreq solution ask <request-file>`: the drafter or a reviewer puts a
8
+ * decision to the owner, on the outcome it is at or the one `--outcome`
9
+ * names (VERB-3.0). A reviewer gives its result once she has answered (3.1).
10
+ * `--approval-request '<action>'`, with no file, asks only whether it may
11
+ * take that one action (DECIDE-3.4).
7
12
  */
8
- export declare function askCommand(file: string, options: WhereOptions): Promise<void>;
13
+ export declare function askCommand(file: string | undefined, options: AskOptions): Promise<void>;
9
14
  //# sourceMappingURL=ask.d.ts.map
@@ -4,24 +4,39 @@ import { pushBeforeRecord } from "./pushing.js";
4
4
  import { readRequest } from "./request.js";
5
5
  import { onOutcome, requireWhere } from "./run-file.js";
6
6
  /**
7
- * `dotreq solution ask <request-file>`: the drafter puts a decision to the
8
- * owner, on the outcome it is drafting or the one `--outcome` names. A
9
- * reviewer escalates instead (`dotreq solution review escalate`), and the
10
- * backend refuses its question (VERB-3.5).
7
+ * `dotreq solution ask <request-file>`: the drafter or a reviewer puts a
8
+ * decision to the owner, on the outcome it is at or the one `--outcome`
9
+ * names (VERB-3.0). A reviewer gives its result once she has answered (3.1).
10
+ * `--approval-request '<action>'`, with no file, asks only whether it may
11
+ * take that one action (DECIDE-3.4).
11
12
  */
12
13
  export async function askCommand(file, options) {
13
- const request = readRequest(file);
14
+ if ((file === undefined) === (options.approvalRequest === undefined)) {
15
+ throw new Error("Ask with a request file, or with --approval-request '<action>' alone: one of them.");
16
+ }
17
+ const asking = file !== undefined
18
+ ? { request: readRequest(file) }
19
+ : { approvalRequest: actionFrom(options.approvalRequest ?? "") };
14
20
  const where = requireWhere(options);
15
21
  const { solutionId, agent } = where;
16
22
  const client = solutionClient();
17
23
  // SOLBRANCH-3.0: the drafter's commits reach the branch before Quinn is asked.
18
24
  await pushBeforeRecord(client, where);
19
- await client.mutation(api.solutions.mutations.ask, {
25
+ const decision = await client.mutation(api.solutions.mutations.ask, {
20
26
  solutionId,
21
27
  agent,
22
28
  ...(where.outcome ? { outcome: where.outcome } : {}),
23
- request,
29
+ ...asking,
24
30
  });
25
- console.log(`Asked${onOutcome(where.outcome)}. It waits on the owner's decision.`);
31
+ console.log(`Asked${onOutcome(where.outcome)}. It waits on the owner's decision${decision ? ` (${decision})` : ""}.`);
32
+ }
33
+ /** The action `--approval-request` names, as JSON. The backend checks it. */
34
+ function actionFrom(text) {
35
+ try {
36
+ return JSON.parse(text);
37
+ }
38
+ catch (error) {
39
+ throw new Error(`--approval-request takes the action as JSON, {"tool": ..., "args": ...}: ${error instanceof Error ? error.message : String(error)}`);
40
+ }
26
41
  }
27
42
  //# sourceMappingURL=ask.js.map
@@ -5,5 +5,5 @@
5
5
  * request is validated against; the CLI can't import the backend, so this
6
6
  * is that text, and a test keeps the two the same.
7
7
  */
8
- export declare const REQUEST_CONTRACT = "Ask the person to decide something about the outcome you are drafting. A choice presents options with a recommendation. One decision per request: if two things need deciding, ask twice.\n\nA request is JSON with a \"kind\" and the fields of that kind:\n\nchoice:\n question (text, up to 200 characters): The question, phrased as a question in the words the person will read it, not as a topic.\n options (list, at least 2): The live alternatives. More than four makes deciding harder; give more only when each is a genuine choice. Leave out anything you have already ruled out.\n name (text, up to 200 characters): The option as you would say it to the person: a noun phrase they can repeat back, not a label.\n benefits (list of text, each up to 200 characters): What is better if this option is taken. One line each, in your own words.\n costs (list of text, each up to 200 characters): What is worse if this option is taken: what it would take to build, and what the person lives with afterwards. Effort is a cost like any other.\n note (text, up to 600 characters, optional): Anything you learned or checked about this option that is not a benefit or a cost. Leave it out when there is nothing to add.\n recommendation (object): Which option you would take, and the one reason why.\n option (text, up to 200 characters): The name of the option you recommend, exactly as it appears in options.\n because (text, up to 200 characters): The one reason that carries it, in one line. The full reasoning belongs in background.\n background (text, up to 4000 characters): What the person needs to know to decide well that the options do not already say. Include only context that would substantially change the quality of the decision. The person reads this after the options, so do not reiterate them.";
8
+ export declare const REQUEST_CONTRACT = "Ask the person to decide something about the outcome you are drafting or reviewing. A choice presents options with a recommendation. One decision per request: if two things need deciding, ask twice.\n\nA request is JSON with a \"kind\" and the fields of that kind:\n\nchoice:\n question (text, up to 200 characters): The question, phrased as a question in the words the person will read it, not as a topic.\n options (list, at least 2): The live alternatives. More than four makes deciding harder; give more only when each is a genuine choice. Leave out anything you have already ruled out.\n name (text, up to 200 characters): The option as you would say it to the person: a noun phrase they can repeat back, not a label.\n benefits (list of text, each up to 200 characters): What is better if this option is taken. One line each, in your own words.\n costs (list of text, each up to 200 characters): What is worse if this option is taken: what it would take to build, and what the person lives with afterwards. Effort is a cost like any other.\n note (text, up to 600 characters, optional): Anything you learned or checked about this option that is not a benefit or a cost. Leave it out when there is nothing to add.\n action (object, optional): An action you may take only with the person's permission, which choosing this option gives. Once they choose it, take it with `dotreq solution approval complete <decision>`. Leave it out for anything you may do yourself.\n tool (text, up to 200 characters): The action, by name. The one that needs the person's permission is \"fix\", starting a fix.\n args (object): The action's arguments, as it takes them: a fix's are below.\n recommendation (object): Which option you would take, and the one reason why.\n option (text, up to 200 characters): The name of the option you recommend, exactly as it appears in options.\n because (text, up to 200 characters): The one reason that carries it, in one line. The full reasoning belongs in background.\n background (text, up to 4000 characters): What the person needs to know to decide well that the options do not already say. Include only context that would substantially change the quality of the decision. The person reads this after the options, so do not reiterate them.\n\nThe args of a \"fix\" action:\n name (text, up to 120 characters): A short name for the fix, from what it puts right, as \"dice below the controls\".\n words (text, up to 2000 characters): What you found, in your own words.\n plan (text, up to 2000 characters): The fix as you would put it to the person, in a sentence or two.\n redo (list, at least 1): The outcomes the fix redoes, and what would change in each.\n outcome (text, up to 200 characters): An outcome to redo, by its name.\n change (text, up to 2000 characters): What would change in it.\n rerun (list of text, each up to 200 characters): The outcomes that await the ones redone and should run again from them. Empty when none should.";
9
9
  //# sourceMappingURL=contract.d.ts.map
@@ -5,7 +5,7 @@
5
5
  * request is validated against; the CLI can't import the backend, so this
6
6
  * is that text, and a test keeps the two the same.
7
7
  */
8
- export const REQUEST_CONTRACT = `Ask the person to decide something about the outcome you are drafting. A choice presents options with a recommendation. One decision per request: if two things need deciding, ask twice.
8
+ export const REQUEST_CONTRACT = `Ask the person to decide something about the outcome you are drafting or reviewing. A choice presents options with a recommendation. One decision per request: if two things need deciding, ask twice.
9
9
 
10
10
  A request is JSON with a "kind" and the fields of that kind:
11
11
 
@@ -16,8 +16,20 @@ choice:
16
16
  benefits (list of text, each up to 200 characters): What is better if this option is taken. One line each, in your own words.
17
17
  costs (list of text, each up to 200 characters): What is worse if this option is taken: what it would take to build, and what the person lives with afterwards. Effort is a cost like any other.
18
18
  note (text, up to 600 characters, optional): Anything you learned or checked about this option that is not a benefit or a cost. Leave it out when there is nothing to add.
19
+ action (object, optional): An action you may take only with the person's permission, which choosing this option gives. Once they choose it, take it with \`dotreq solution approval complete <decision>\`. Leave it out for anything you may do yourself.
20
+ tool (text, up to 200 characters): The action, by name. The one that needs the person's permission is "fix", starting a fix.
21
+ args (object): The action's arguments, as it takes them: a fix's are below.
19
22
  recommendation (object): Which option you would take, and the one reason why.
20
23
  option (text, up to 200 characters): The name of the option you recommend, exactly as it appears in options.
21
24
  because (text, up to 200 characters): The one reason that carries it, in one line. The full reasoning belongs in background.
22
- background (text, up to 4000 characters): What the person needs to know to decide well that the options do not already say. Include only context that would substantially change the quality of the decision. The person reads this after the options, so do not reiterate them.`;
25
+ background (text, up to 4000 characters): What the person needs to know to decide well that the options do not already say. Include only context that would substantially change the quality of the decision. The person reads this after the options, so do not reiterate them.
26
+
27
+ The args of a "fix" action:
28
+ name (text, up to 120 characters): A short name for the fix, from what it puts right, as "dice below the controls".
29
+ words (text, up to 2000 characters): What you found, in your own words.
30
+ plan (text, up to 2000 characters): The fix as you would put it to the person, in a sentence or two.
31
+ redo (list, at least 1): The outcomes the fix redoes, and what would change in each.
32
+ outcome (text, up to 200 characters): An outcome to redo, by its name.
33
+ change (text, up to 2000 characters): What would change in it.
34
+ rerun (list of text, each up to 200 characters): The outcomes that await the ones redone and should run again from them. Empty when none should.`;
23
35
  //# sourceMappingURL=contract.js.map
@@ -1,4 +1,5 @@
1
1
  import { Option } from "commander";
2
+ import { approvalCompleteCommand } from "./approval.js";
2
3
  import { askCommand } from "./ask.js";
3
4
  import { authorCommand } from "./author.js";
4
5
  import { checkoutCommand, pushCommand } from "./checkout.js";
@@ -139,8 +140,15 @@ The outcome waits until that is fixed and you are started again.
139
140
  const ASK_EXAMPLES = `
140
141
  Examples:
141
142
  dotreq solution ask question.json
143
+ dotreq solution ask --approval-request '{"tool": "fix", "args": {"name": "Receipt email required", "words": "...", "plan": "...", "redo": [{"outcome": "Code", "change": "..."}], "rerun": []}}'
142
144
 
143
- A reviewer doesn't ask: it escalates, with dotreq solution review escalate --comment "...".
145
+ A reviewer asks the same way, then gives its result once the owner has answered: it is
146
+ sent for again then.
147
+
148
+ An option may carry an action you may take only with the owner's permission: starting a
149
+ fix, as "fix". Choosing that option gives it; nothing is done until you take it, with
150
+ dotreq solution approval complete <decision>. --approval-request asks only whether you may
151
+ take one action.
144
152
  `;
145
153
  /**
146
154
  * What each result does, on `review --help` (STEP-REVIEW-1, 2), and the
@@ -151,8 +159,10 @@ const REVIEW_RESULTS = `
151
159
  Results:
152
160
  approved The work moves on.
153
161
  approved-with-revisions What remains is small: the drafter fixes it once, and it moves on without another review.
154
- needs-revision The drafter revises and you read it again. Not recorded from the twelfth round.
155
- escalate The owner decides: approve it, or send it back. Your comment is the note they read.
162
+ needs-revision The drafter revises and you read it again. From the twelfth round, give it only once the owner has chosen it.
163
+
164
+ When whether to approve turns on the owner's own judgement, put a decision to them first
165
+ with dotreq solution ask, and give your result once they have answered.
156
166
 
157
167
  When you approve, write an executive summary for the owner with --executive-summary (or
158
168
  --summary), describing the work as it will stand once any fixes you asked for are made. If
@@ -173,7 +183,6 @@ needs-revision, with no video. Only a plain approval carries a video.
173
183
 
174
184
  Examples:
175
185
  dotreq solution review approved --comment "Checked the pitch against the board; the customer is named." --executive-summary "A pitch for guest checkout, for first-time buyers. Look at the appetite: two weeks."
176
- dotreq solution review escalate --comment "Whether to name a segment is your call: the drafter and I disagree."
177
186
  dotreq solution review needs-revision --comment "The risk score gives no reason."
178
187
  dotreq solution review approved "video:demo/final.mp4 .dotreq/demo-scripts/steps.json" --comment "Recorded it; step 3 now waits for the receipt." --executive-summary "A two-minute demo of guest checkout. Look at step 4: the receipt."
179
188
  `;
@@ -253,8 +262,8 @@ export function registerSolution(program) {
253
262
  .action(wrap(prWaitCommand));
254
263
  whereOptions(solution
255
264
  .command("review <result> [video...]", HIDDEN)
256
- .description("Record the review's result: approved, approved-with-revisions, needs-revision or escalate"))
257
- .option("--comment <text>", "What should change, what you checked, or what the owner is to decide: required for every result")
265
+ .description("Record the review's result: approved, approved-with-revisions or needs-revision"))
266
+ .option("--comment <text>", "What should change, or what you checked: required for every result")
258
267
  .option("--executive-summary <text>", "For the owner, with every approval (required there)")
259
268
  .option("--summary <text>", "Short for --executive-summary")
260
269
  .addHelpText("after", REVIEW_RESULTS)
@@ -265,14 +274,24 @@ export function registerSolution(program) {
265
274
  .option("--keep", "Leave the scenario's earlier runs in place, to review the new run beside them")
266
275
  .action(wrap(rehearseCommand));
267
276
  whereOptions(solution
268
- .command("ask <request-file>", HIDDEN)
269
- .description("Put a decision to the owner, as JSON in the named file; the drafter's, since a reviewer escalates"))
277
+ .command("ask [request-file]", HIDDEN)
278
+ .description("Put a decision to the owner, as JSON in the named file, drafting or reviewing"))
279
+ .option("--approval-request <action>", 'Ask only whether you may take one action, given as JSON: {"tool": "fix", "args": {...}}. No request file then')
270
280
  .addHelpText("after", `\n${REQUEST_CONTRACT}\n${ASK_EXAMPLES}`)
271
281
  .action(wrap(askCommand));
282
+ const approval = solution
283
+ .command("approval", HIDDEN)
284
+ .description("An action the owner allowed by choosing the option that carries it");
285
+ approval
286
+ .command("complete <decision>")
287
+ .description("Take the action on the option the owner chose, exactly as offered, once, and print what came of it")
288
+ .option("--solution <id>", "Which solution, when the run does not say (inside a runner it does)")
289
+ .option("--agent <name>", "Which agent you are, when the run does not say (inside a runner it does)")
290
+ .action(wrap(approvalCompleteCommand));
272
291
  whereOptions(solution
273
292
  .command("thread", HIDDEN)
274
293
  .description("The outcome's thread, oldest first: every submission, review, decision, stall and restart on it"))
275
- .option("--latest [n]", "Only the newest n submission notes, review results, escalations and decisions, still oldest first; alone, the newest one. Stalls, restarts and redrafts are left out")
294
+ .option("--latest [n]", "Only the newest n submission notes, review results and decisions, still oldest first; alone, the newest one. Stalls, restarts and redrafts are left out")
276
295
  .action(wrap(threadCommand));
277
296
  whereOptions(solution
278
297
  .command("things <name>", HIDDEN)
@@ -1,7 +1,7 @@
1
1
  import { type WhereOptions } from "./run-file.js";
2
2
  import { type SummaryOptions } from "./summary.js";
3
3
  export type ReviewOptions = WhereOptions & SummaryOptions & {
4
- /** What should change, what was checked, or what the owner is to decide: required for every result (VERB-2.0, 2.1, 2.4). */
4
+ /** What should change, or what was checked: required for every result (VERB-2.0, 2.1). */
5
5
  comment?: string;
6
6
  /**
7
7
  * With an approval of a demo's script: the video recorded from it and
@@ -14,9 +14,10 @@ export type ReviewOptions = WhereOptions & SummaryOptions & {
14
14
  * `dotreq solution review <result> --comment "..."`: the reviewer's
15
15
  * result on the outcome it is reviewing, or on the one `--outcome` names.
16
16
  * The comment is what the next reader of the thread sees first — the
17
- * drafter works from it, and on an escalation it is the note the owner
18
- * reads — so every result is refused without one before anything is
19
- * recorded (VERB-2.0, 2.1, 2.4). An approval also carries an executive
17
+ * drafter works from it — so every result is refused without one before
18
+ * anything is recorded (VERB-2.0, 2.1). When the reviewer needs the
19
+ * owner's judgement it asks first (`dotreq solution ask`), and gives its
20
+ * result once she has answered; escalate is refused, saying so (2.4). An approval also carries an executive
20
21
  * summary, `--executive-summary "..."`, which is what the owner reads with
21
22
  * the work (VERB-2.5); the outcome's own rules decide what follows. An
22
23
  * approval of a demo's script carries the video the reviewer recorded from
@@ -10,15 +10,17 @@ const RESULTS = {
10
10
  approved: "approved",
11
11
  "approved-with-revisions": "approved with revisions",
12
12
  "needs-revision": "needs revision",
13
- escalate: "escalate",
14
13
  };
14
+ /** What a reviewer is told when it gives escalate, which is no longer a result (VERB-2.4): in the backend's words. */
15
+ const NO_ESCALATING = "Nothing was recorded: escalate is no longer a result. When you need the owner's judgement, put a decision to them with `dotreq solution ask` (`dotreq solution ask --help` shows the form), and give your result once they have answered.";
15
16
  /**
16
17
  * `dotreq solution review <result> --comment "..."`: the reviewer's
17
18
  * result on the outcome it is reviewing, or on the one `--outcome` names.
18
19
  * The comment is what the next reader of the thread sees first — the
19
- * drafter works from it, and on an escalation it is the note the owner
20
- * reads — so every result is refused without one before anything is
21
- * recorded (VERB-2.0, 2.1, 2.4). An approval also carries an executive
20
+ * drafter works from it — so every result is refused without one before
21
+ * anything is recorded (VERB-2.0, 2.1). When the reviewer needs the
22
+ * owner's judgement it asks first (`dotreq solution ask`), and gives its
23
+ * result once she has answered; escalate is refused, saying so (2.4). An approval also carries an executive
22
24
  * summary, `--executive-summary "..."`, which is what the owner reads with
23
25
  * the work (VERB-2.5); the outcome's own rules decide what follows. An
24
26
  * approval of a demo's script carries the video the reviewer recorded from
@@ -26,17 +28,17 @@ const RESULTS = {
26
28
  * (SIGHT-13.2); no other result carries one (13.4.0).
27
29
  */
28
30
  export async function reviewCommand(result, options) {
31
+ if (result === "escalate")
32
+ throw new Error(NO_ESCALATING);
29
33
  const outcome = RESULTS[result];
30
34
  if (!outcome) {
31
- throw new Error(`A review result is approved, approved-with-revisions, needs-revision or escalate, not "${result}".`);
35
+ throw new Error(`A review result is approved, approved-with-revisions or needs-revision, not "${result}".`);
32
36
  }
33
37
  const comment = options.comment?.trim();
34
38
  if (!comment) {
35
39
  throw new Error(outcome === "approved"
36
40
  ? "Say what you checked: your comment is what the next reader sees first."
37
- : outcome === "escalate"
38
- ? "Say what the owner is to decide: your comment is the note they read with the work."
39
- : "Say what should change: the drafter works from your comment.");
41
+ : "Say what should change: the drafter works from your comment.");
40
42
  }
41
43
  // VERB-2.5, 2.5.0: an approval carries an executive summary for the
42
44
  // owner, and nothing else does; refused before anything is recorded, in
@@ -47,7 +49,7 @@ export async function reviewCommand(result, options) {
47
49
  throw new Error("Write an executive summary for the owner with --executive-summary: it's what they read first. Say what this is and what most needs their eye, as the work will stand once any fixes you asked for are made.");
48
50
  }
49
51
  if (!approving && summary) {
50
- throw new Error(`Only an approval carries an executive summary, so nothing was recorded. Leave --executive-summary off: ${outcome === "escalate" ? "your comment is the note the owner reads." : "the drafter works from your comment."}`);
52
+ throw new Error("Only an approval carries an executive summary, so nothing was recorded. Leave --executive-summary off: the drafter works from your comment.");
51
53
  }
52
54
  // SIGHT-13.2, 13.4.0: a video goes only with a plain approval, and is
53
55
  // read before anything is pushed, uploaded or recorded.
@@ -55,7 +57,7 @@ export async function reviewCommand(result, options) {
55
57
  if (recordings.length > 0 && outcome !== "approved") {
56
58
  throw new Error(outcome === "approved with revisions"
57
59
  ? "Nothing was recorded: a demo's script isn't approved with revisions, because its drafter can't change the script without the video going out of step. Fix anything small yourself, record the video, and approve with it, saying in your comment what you changed; or give needs-revision."
58
- : `Nothing was recorded: only an approval carries a video. Give your result without it: ${outcome === "escalate" ? "your comment is the note the owner reads" : "the drafter works from your comment"}, and no video is kept.`);
60
+ : "Nothing was recorded: only an approval carries a video. Give your result without it: the drafter works from your comment, and no video is kept.");
59
61
  }
60
62
  const where = requireWhere(options);
61
63
  const { solutionId, agent } = where;
@@ -83,9 +85,7 @@ export async function reviewCommand(result, options) {
83
85
  // again once the reviewer has looked.
84
86
  if (said?.refused)
85
87
  throw new Error(said.refused);
86
- console.log(outcome === "escalate"
87
- ? `Escalated${onOutcome(where.outcome)}. It waits on the owner's decision.`
88
- : `Recorded your review${onOutcome(where.outcome)}: ${outcome}.`);
88
+ console.log(`Recorded your review${onOutcome(where.outcome)}: ${outcome}.`);
89
89
  }
90
90
  /**
91
91
  * The videos a review carries (SIGHT-13.2): each recorded from a demo's
@@ -104,10 +104,11 @@ async function reviewerOwes(client, solutionId, outcome, agent) {
104
104
  if (entry.kind === "submission")
105
105
  since = at + 1;
106
106
  });
107
+ // Only a review is a result: a question it put to the owner, or an
108
+ // escalation withdrawn when escalating went, leaves it still owing one.
107
109
  return !thread
108
110
  .slice(since)
109
- .some((entry) => entry.agent === agent &&
110
- (entry.kind === "review" || entry.kind === "escalation"));
111
+ .some((entry) => entry.agent === agent && entry.kind === "review");
111
112
  }
112
113
  /**
113
114
  * `dotreq solution status`: where the outcome the agent is at stands, as
@@ -4,7 +4,7 @@ import { requireWhere } from "./run-file.js";
4
4
  import { outcomeFor } from "./status.js";
5
5
  /**
6
6
  * What `--latest` counts: what someone wrote — a submission note, a review
7
- * result, an escalation or a decision. A stall, a restart and a redraft
7
+ * result, a decision put to the person or their answer. A stall, a restart and a redraft
8
8
  * because the outcome it is on was redrafted are on the thread but not
9
9
  * counted, so a drafter revising after a restart reads the review it is
10
10
  * answering, not the restart (VERB-6.4, 6.5).
package/dist/convex.d.ts CHANGED
@@ -99,6 +99,7 @@ export declare const api: {
99
99
  next: import("convex/server").FunctionReference<"mutation", "public", any, any, string | undefined>;
100
100
  complete: import("convex/server").FunctionReference<"mutation", "public", any, any, string | undefined>;
101
101
  ask: import("convex/server").FunctionReference<"mutation", "public", any, any, string | undefined>;
102
+ completeApproval: import("convex/server").FunctionReference<"mutation", "public", any, any, string | undefined>;
102
103
  stall: import("convex/server").FunctionReference<"mutation", "public", any, any, string | undefined>;
103
104
  answer: import("convex/server").FunctionReference<"mutation", "public", any, any, string | undefined>;
104
105
  approveCheckpoint: import("convex/server").FunctionReference<"mutation", "public", any, any, string | undefined>;
package/dist/convex.js CHANGED
@@ -112,6 +112,8 @@ export const api = {
112
112
  next: mutation("solutions/mutations:next"),
113
113
  complete: mutation("solutions/mutations:complete"),
114
114
  ask: mutation("solutions/mutations:ask"),
115
+ // DECIDE-4: the action on the option the owner chose, taken once.
116
+ completeApproval: mutation("solutions/mutations:completeApproval"),
115
117
  stall: mutation("solutions/mutations:stall"),
116
118
  answer: mutation("solutions/mutations:answer"),
117
119
  // A rehearsal's scripted approval of a checkpoint (CHECKPOINT-1.1).
@@ -142,16 +142,15 @@ export type Script = {
142
142
  /**
143
143
  * Give this review result (reviewing), with the comment every result
144
144
  * carries. An approval, with or without revisions, carries an executive
145
- * summary (VERB-2.5), and needs revision and escalate carry none; a
146
- * script that breaks either is refused, as the agent would be
147
- * (REHEARSE-1.1).
145
+ * summary (VERB-2.5), and needs revision carries none; a script that
146
+ * breaks either is refused, as the agent would be (REHEARSE-1.1).
148
147
  */
149
148
  review?: {
150
- outcome: "approved" | "approved with revisions" | "needs revision" | "escalate";
149
+ outcome: "approved" | "approved with revisions" | "needs revision";
151
150
  comment: string;
152
151
  summary?: string;
153
152
  };
154
- /** Put a decision to the person instead (drafting). */
153
+ /** Put a decision to the person instead, drafting or reviewing; a reviewer gives its result once she answers. */
155
154
  ask?: unknown;
156
155
  /** Stall instead. */
157
156
  stall?: string;
@@ -160,7 +159,7 @@ export type Script = {
160
159
  export type Party = "real" | Script | Script[];
161
160
  /**
162
161
  * What the person answers, in order asked: by an outcome's name, her answer
163
- * to a question or an escalation on it; by a checkpoint's name, her
162
+ * to a decision its drafter or a reviewer put to her; by a checkpoint's name, her
164
163
  * decision there, which a rehearsal gives only as an approval.
165
164
  */
166
165
  export type PersonScript = Record<string, Array<{
@@ -296,7 +295,7 @@ export type RehearsalClient = {
296
295
  /** With `made`: the drafter's executive summary (VERB-1.6). */
297
296
  summary?: string;
298
297
  review?: {
299
- outcome: "approved" | "approved with revisions" | "needs revision" | "escalate";
298
+ outcome: "approved" | "approved with revisions" | "needs revision";
300
299
  comment: string;
301
300
  /** With an approval: the executive summary (VERB-2.5). */
302
301
  summary?: string;
@@ -314,7 +313,7 @@ export type RehearsalClient = {
314
313
  outcome: string;
315
314
  reason: string;
316
315
  }): Promise<void>;
317
- /** The decision open on an outcome — a question or an escalation — or null. Absent when the client can't answer for the person. */
316
+ /** The decision open on an outcome, its drafter's or a reviewer's, or null. Absent when the client can't answer for the person. */
318
317
  openDecision?(solutionId: string, outcome: string): Promise<{
319
318
  id: string;
320
319
  } | null>;
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@popoverai/dotrequirements",
3
- "version": "0.35.0",
3
+ "version": "0.36.0",
4
4
  "description": "Requirements as testable, human-readable data — CLI and test harness for spec-driven development with AI coding agents",
5
5
  "type": "module",
6
6
  "bin": {