@popoverai/dotrequirements 0.35.0 → 0.36.1
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.
- package/dist/commands/solution/approval.d.ts +9 -0
- package/dist/commands/solution/approval.js +19 -0
- package/dist/commands/solution/ask.d.ts +10 -5
- package/dist/commands/solution/ask.js +23 -8
- package/dist/commands/solution/contract.d.ts +1 -1
- package/dist/commands/solution/contract.js +14 -2
- package/dist/commands/solution/index.js +41 -16
- package/dist/commands/solution/pull-request.d.ts +10 -10
- package/dist/commands/solution/pull-request.js +45 -11
- package/dist/commands/solution/review.d.ts +5 -4
- package/dist/commands/solution/review.js +13 -13
- package/dist/commands/solution/status.js +3 -2
- package/dist/commands/solution/thread.js +1 -1
- package/dist/convex.d.ts +1 -0
- package/dist/convex.js +2 -0
- package/dist/rehearsal/types.d.ts +7 -8
- package/package.json +1 -1
|
@@ -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
|
|
4
|
-
* owner, on the outcome it is
|
|
5
|
-
* reviewer
|
|
6
|
-
*
|
|
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:
|
|
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
|
|
8
|
-
* owner, on the outcome it is
|
|
9
|
-
* reviewer
|
|
10
|
-
*
|
|
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
|
-
|
|
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
|
-
|
|
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";
|
|
@@ -101,11 +102,17 @@ Reading it changes nothing.
|
|
|
101
102
|
`;
|
|
102
103
|
/** What `pr wait` does, on its `--help` (PULL-5, PULL-6). */
|
|
103
104
|
const PR_WAIT_HELP = `
|
|
104
|
-
For a reviewer,
|
|
105
|
-
|
|
106
|
-
|
|
107
|
-
|
|
108
|
-
|
|
105
|
+
For a reviewer, while the pull request's checks run or GitHub works out whether it
|
|
106
|
+
can merge. Records where it stands now; then end your session without a result.
|
|
107
|
+
You are started again, in this session, once GitHub reports it clean, once it is
|
|
108
|
+
merged or closed, or when the wait is up: ten minutes unless you say, up to an
|
|
109
|
+
hour. A conflict, a failed check or a branch behind doesn't change by waiting:
|
|
110
|
+
judge it now. A new submission ends the wait too, and you review that as you
|
|
111
|
+
would any other.
|
|
112
|
+
|
|
113
|
+
Examples:
|
|
114
|
+
dotreq solution pr wait
|
|
115
|
+
dotreq solution pr wait 30m
|
|
109
116
|
`;
|
|
110
117
|
/** What `pr open` does, on its `--help`, where `author --help` sends the agent (PULL-2). */
|
|
111
118
|
const PULL_REQUEST_HELP = `
|
|
@@ -139,8 +146,15 @@ The outcome waits until that is fixed and you are started again.
|
|
|
139
146
|
const ASK_EXAMPLES = `
|
|
140
147
|
Examples:
|
|
141
148
|
dotreq solution ask question.json
|
|
149
|
+
dotreq solution ask --approval-request '{"tool": "fix", "args": {"name": "Receipt email required", "words": "...", "plan": "...", "redo": [{"outcome": "Code", "change": "..."}], "rerun": []}}'
|
|
150
|
+
|
|
151
|
+
A reviewer asks the same way, then gives its result once the owner has answered: it is
|
|
152
|
+
sent for again then.
|
|
142
153
|
|
|
143
|
-
|
|
154
|
+
An option may carry an action you may take only with the owner's permission: starting a
|
|
155
|
+
fix, as "fix". Choosing that option gives it; nothing is done until you take it, with
|
|
156
|
+
dotreq solution approval complete <decision>. --approval-request asks only whether you may
|
|
157
|
+
take one action.
|
|
144
158
|
`;
|
|
145
159
|
/**
|
|
146
160
|
* What each result does, on `review --help` (STEP-REVIEW-1, 2), and the
|
|
@@ -151,8 +165,10 @@ const REVIEW_RESULTS = `
|
|
|
151
165
|
Results:
|
|
152
166
|
approved The work moves on.
|
|
153
167
|
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.
|
|
155
|
-
|
|
168
|
+
needs-revision The drafter revises and you read it again. From the twelfth round, give it only once the owner has chosen it.
|
|
169
|
+
|
|
170
|
+
When whether to approve turns on the owner's own judgement, put a decision to them first
|
|
171
|
+
with dotreq solution ask, and give your result once they have answered.
|
|
156
172
|
|
|
157
173
|
When you approve, write an executive summary for the owner with --executive-summary (or
|
|
158
174
|
--summary), describing the work as it will stand once any fixes you asked for are made. If
|
|
@@ -173,7 +189,6 @@ needs-revision, with no video. Only a plain approval carries a video.
|
|
|
173
189
|
|
|
174
190
|
Examples:
|
|
175
191
|
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
192
|
dotreq solution review needs-revision --comment "The risk score gives no reason."
|
|
178
193
|
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
194
|
`;
|
|
@@ -247,14 +262,14 @@ export function registerSolution(program) {
|
|
|
247
262
|
.addHelpText("after", PR_STATUS_HELP)
|
|
248
263
|
.action(wrap(prStatusCommand));
|
|
249
264
|
whereOptions(pr
|
|
250
|
-
.command("wait")
|
|
251
|
-
.description("Wait on the pull request with your session ended: you start again
|
|
265
|
+
.command("wait [duration]")
|
|
266
|
+
.description("Wait on the pull request with your session ended, for ten minutes unless you say (as 5m or 1h, up to an hour): you start again once it is clean, merged or closed, or when the time is up"))
|
|
252
267
|
.addHelpText("after", PR_WAIT_HELP)
|
|
253
268
|
.action(wrap(prWaitCommand));
|
|
254
269
|
whereOptions(solution
|
|
255
270
|
.command("review <result> [video...]", HIDDEN)
|
|
256
|
-
.description("Record the review's result: approved, approved-with-revisions
|
|
257
|
-
.option("--comment <text>", "What should change, what you checked
|
|
271
|
+
.description("Record the review's result: approved, approved-with-revisions or needs-revision"))
|
|
272
|
+
.option("--comment <text>", "What should change, or what you checked: required for every result")
|
|
258
273
|
.option("--executive-summary <text>", "For the owner, with every approval (required there)")
|
|
259
274
|
.option("--summary <text>", "Short for --executive-summary")
|
|
260
275
|
.addHelpText("after", REVIEW_RESULTS)
|
|
@@ -265,14 +280,24 @@ export function registerSolution(program) {
|
|
|
265
280
|
.option("--keep", "Leave the scenario's earlier runs in place, to review the new run beside them")
|
|
266
281
|
.action(wrap(rehearseCommand));
|
|
267
282
|
whereOptions(solution
|
|
268
|
-
.command("ask
|
|
269
|
-
.description("Put a decision to the owner, as JSON in the named file
|
|
283
|
+
.command("ask [request-file]", HIDDEN)
|
|
284
|
+
.description("Put a decision to the owner, as JSON in the named file, drafting or reviewing"))
|
|
285
|
+
.option("--approval-request <action>", 'Ask only whether you may take one action, given as JSON: {"tool": "fix", "args": {...}}. No request file then')
|
|
270
286
|
.addHelpText("after", `\n${REQUEST_CONTRACT}\n${ASK_EXAMPLES}`)
|
|
271
287
|
.action(wrap(askCommand));
|
|
288
|
+
const approval = solution
|
|
289
|
+
.command("approval", HIDDEN)
|
|
290
|
+
.description("An action the owner allowed by choosing the option that carries it");
|
|
291
|
+
approval
|
|
292
|
+
.command("complete <decision>")
|
|
293
|
+
.description("Take the action on the option the owner chose, exactly as offered, once, and print what came of it")
|
|
294
|
+
.option("--solution <id>", "Which solution, when the run does not say (inside a runner it does)")
|
|
295
|
+
.option("--agent <name>", "Which agent you are, when the run does not say (inside a runner it does)")
|
|
296
|
+
.action(wrap(approvalCompleteCommand));
|
|
272
297
|
whereOptions(solution
|
|
273
298
|
.command("thread", HIDDEN)
|
|
274
299
|
.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
|
|
300
|
+
.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
301
|
.action(wrap(threadCommand));
|
|
277
302
|
whereOptions(solution
|
|
278
303
|
.command("things <name>", HIDDEN)
|
|
@@ -25,10 +25,7 @@ export type PullRequestState = {
|
|
|
25
25
|
}>;
|
|
26
26
|
head: string;
|
|
27
27
|
};
|
|
28
|
-
/**
|
|
29
|
-
* Where the pull request stood, as a wait records it (PULL-6.4): what a
|
|
30
|
-
* reviewer judges by, so a later read that differs is a change.
|
|
31
|
-
*/
|
|
28
|
+
/** Where the pull request stood, in words, as a wait records it beside what the line shows (PULL-5.1.0). */
|
|
32
29
|
export declare function seenOf(state: PullRequestState): string;
|
|
33
30
|
/** Where the pull request stands, as a reviewer reads it (PULL-4). */
|
|
34
31
|
export declare function describeState(state: PullRequestState): string;
|
|
@@ -39,14 +36,17 @@ export declare function describeState(state: PullRequestState): string;
|
|
|
39
36
|
export declare function prStatusCommand(options: WhereOptions & {
|
|
40
37
|
json?: boolean;
|
|
41
38
|
}): Promise<void>;
|
|
39
|
+
/** A wait's length as an agent types it, `90s`, `5m` or `1h`, in milliseconds; refused past an hour or at none (PULL-5.8 to 5.10). */
|
|
40
|
+
export declare function waitFor(duration: string | undefined): number;
|
|
42
41
|
/**
|
|
43
|
-
* `dotreq solution pr wait`: the reviewer waits on the pull
|
|
44
|
-
* no session running (PULL-5)
|
|
45
|
-
* and its session can end without a
|
|
46
|
-
*
|
|
47
|
-
* (PULL-6).
|
|
42
|
+
* `dotreq solution pr wait [duration]`: the reviewer waits on the pull
|
|
43
|
+
* request with no session running (PULL-5), ten minutes unless it says, up
|
|
44
|
+
* to an hour: it records what it saw, and its session can end without a
|
|
45
|
+
* result. It starts again, in the same session, once the pull request is
|
|
46
|
+
* `clean`, once it is merged or closed, or when the wait is up (PULL-6). A
|
|
47
|
+
* pull request already `clean` needs no wait (5.11).
|
|
48
48
|
*/
|
|
49
|
-
export declare function prWaitCommand(options: WhereOptions): Promise<void>;
|
|
49
|
+
export declare function prWaitCommand(duration: string | undefined, options: WhereOptions): Promise<void>;
|
|
50
50
|
/**
|
|
51
51
|
* `dotreq solution pr open`: the solution's pull request, opened or
|
|
52
52
|
* found (PULL-2). Inside a runner the branch is pushed first, as a submission
|
|
@@ -7,10 +7,7 @@ import { requireWhere } from "./run-file.js";
|
|
|
7
7
|
import { outcomeFor } from "./status.js";
|
|
8
8
|
/** The command that opens the solution's pull request and prints its reference. */
|
|
9
9
|
export const OPEN_IT = "dotreq solution pr open";
|
|
10
|
-
/**
|
|
11
|
-
* Where the pull request stood, as a wait records it (PULL-6.4): what a
|
|
12
|
-
* reviewer judges by, so a later read that differs is a change.
|
|
13
|
-
*/
|
|
10
|
+
/** Where the pull request stood, in words, as a wait records it beside what the line shows (PULL-5.1.0). */
|
|
14
11
|
export function seenOf(state) {
|
|
15
12
|
return [
|
|
16
13
|
state.head,
|
|
@@ -72,26 +69,63 @@ export async function prStatusCommand(options) {
|
|
|
72
69
|
const state = await stateFor(requireWhere(options));
|
|
73
70
|
console.log(options.json ? JSON.stringify(state) : describeState(state));
|
|
74
71
|
}
|
|
72
|
+
/** What a reviewer is told when it asks for a wait it can't have (PULL-5.10), in the backend's words. */
|
|
73
|
+
const WAIT_REFUSED = "Nothing is waiting: a wait is up to an hour, ten minutes unless you say, as `dotreq solution pr wait 5m` or `pr wait 1h`.";
|
|
74
|
+
/** A wait's length as an agent types it, `90s`, `5m` or `1h`, in milliseconds; refused past an hour or at none (PULL-5.8 to 5.10). */
|
|
75
|
+
export function waitFor(duration) {
|
|
76
|
+
if (duration === undefined)
|
|
77
|
+
return 10 * 60 * 1000;
|
|
78
|
+
const found = /^(\d+)(s|m|h)$/.exec(duration.trim());
|
|
79
|
+
const unit = { s: 1000, m: 60 * 1000, h: 60 * 60 * 1000 };
|
|
80
|
+
const ms = found ? Number(found[1]) * unit[found[2]] : 0;
|
|
81
|
+
if (!(ms > 0 && ms <= 60 * 60 * 1000))
|
|
82
|
+
throw new Error(WAIT_REFUSED);
|
|
83
|
+
return ms;
|
|
84
|
+
}
|
|
85
|
+
/** In words, how long a wait is. */
|
|
86
|
+
function forHowLong(ms) {
|
|
87
|
+
if (ms === 3_600_000)
|
|
88
|
+
return "an hour";
|
|
89
|
+
if (ms % 60_000 !== 0) {
|
|
90
|
+
const seconds = Math.round(ms / 1000);
|
|
91
|
+
return `${seconds} second${seconds === 1 ? "" : "s"}`;
|
|
92
|
+
}
|
|
93
|
+
const minutes = ms / 60_000;
|
|
94
|
+
return `${minutes} minute${minutes === 1 ? "" : "s"}`;
|
|
95
|
+
}
|
|
75
96
|
/**
|
|
76
|
-
* `dotreq solution pr wait`: the reviewer waits on the pull
|
|
77
|
-
* no session running (PULL-5)
|
|
78
|
-
* and its session can end without a
|
|
79
|
-
*
|
|
80
|
-
* (PULL-6).
|
|
97
|
+
* `dotreq solution pr wait [duration]`: the reviewer waits on the pull
|
|
98
|
+
* request with no session running (PULL-5), ten minutes unless it says, up
|
|
99
|
+
* to an hour: it records what it saw, and its session can end without a
|
|
100
|
+
* result. It starts again, in the same session, once the pull request is
|
|
101
|
+
* `clean`, once it is merged or closed, or when the wait is up (PULL-6). A
|
|
102
|
+
* pull request already `clean` needs no wait (5.11).
|
|
81
103
|
*/
|
|
82
|
-
export async function prWaitCommand(options) {
|
|
104
|
+
export async function prWaitCommand(duration, options) {
|
|
105
|
+
const forMs = waitFor(duration);
|
|
83
106
|
const where = requireWhere(options);
|
|
84
107
|
const state = await stateFor(where);
|
|
108
|
+
if (state.mergeState === "clean") {
|
|
109
|
+
throw new Error(`Nothing is waiting: the pull request is already \`clean\`.\n\n${describeState(state)}`);
|
|
110
|
+
}
|
|
85
111
|
await solutionClient().mutation(api.solutions.waits.waitOnPullRequest, {
|
|
86
112
|
solutionId: where.solutionId,
|
|
87
113
|
agent: where.agent,
|
|
88
114
|
...(where.outcome ? { outcome: where.outcome } : {}),
|
|
89
115
|
seen: seenOf(state),
|
|
116
|
+
saw: {
|
|
117
|
+
number: state.number,
|
|
118
|
+
url: state.url,
|
|
119
|
+
conflicting: state.mergeable === "conflicting",
|
|
120
|
+
failed: state.failed.length,
|
|
121
|
+
running: state.running.length,
|
|
122
|
+
},
|
|
123
|
+
forMs,
|
|
90
124
|
});
|
|
91
125
|
console.log([
|
|
92
126
|
describeState(state),
|
|
93
127
|
"",
|
|
94
|
-
|
|
128
|
+
`Waiting on it. End your session now: you'll be started again, here, once it is \`clean\`, once it is merged or closed, or in ${forHowLong(forMs)}, whichever comes first.`,
|
|
95
129
|
].join("\n"));
|
|
96
130
|
}
|
|
97
131
|
/**
|
|
@@ -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
|
|
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
|
|
18
|
-
*
|
|
19
|
-
*
|
|
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
|
|
20
|
-
*
|
|
21
|
-
*
|
|
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
|
|
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
|
-
:
|
|
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(
|
|
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
|
-
:
|
|
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
|
|
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,
|
|
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
|
|
146
|
-
*
|
|
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"
|
|
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
|
|
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
|
|
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"
|
|
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
|
|
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