@skrr-ai/cli 0.1.14 → 0.1.17
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/README.md +3 -0
- package/dist/base-command.d.ts +14 -0
- package/dist/base-command.js +60 -1
- package/dist/commands/agents/create.d.ts +1 -0
- package/dist/commands/agents/create.js +6 -1
- package/dist/commands/agents/instructions/add.d.ts +21 -0
- package/dist/commands/agents/instructions/add.js +115 -0
- package/dist/commands/agents/instructions/disable.d.ts +13 -0
- package/dist/commands/agents/instructions/disable.js +50 -0
- package/dist/commands/agents/instructions/edit.d.ts +16 -0
- package/dist/commands/agents/instructions/edit.js +79 -0
- package/dist/commands/agents/instructions/enable.d.ts +13 -0
- package/dist/commands/agents/instructions/enable.js +50 -0
- package/dist/commands/agents/instructions/move.d.ts +15 -0
- package/dist/commands/agents/instructions/move.js +61 -0
- package/dist/commands/agents/instructions/preview.d.ts +13 -0
- package/dist/commands/agents/instructions/preview.js +59 -0
- package/dist/commands/agents/instructions/remove.d.ts +13 -0
- package/dist/commands/agents/instructions/remove.js +49 -0
- package/dist/commands/agents/instructions/set.d.ts +17 -0
- package/dist/commands/agents/instructions/set.js +79 -0
- package/dist/commands/agents/instructions/show.d.ts +15 -0
- package/dist/commands/agents/instructions/show.js +76 -0
- package/dist/commands/agents/instructions/upgrade.d.ts +16 -0
- package/dist/commands/agents/instructions/upgrade.js +77 -0
- package/dist/commands/agents/show.js +5 -0
- package/dist/commands/agents/triggers/policy.js +8 -2
- package/dist/commands/agents/update.d.ts +1 -0
- package/dist/commands/agents/update.js +8 -2
- package/dist/commands/commitments/autonomy.js +9 -1
- package/dist/commands/commitments/create.d.ts +8 -5
- package/dist/commands/commitments/create.js +38 -8
- package/dist/commands/commitments/doctor.d.ts +19 -0
- package/dist/commands/commitments/doctor.js +72 -0
- package/dist/commands/commitments/preflight.d.ts +3 -1
- package/dist/commands/commitments/preflight.js +25 -3
- package/dist/commands/commitments/reports.js +15 -2
- package/dist/commands/commitments/update.d.ts +9 -2
- package/dist/commands/commitments/update.js +36 -2
- package/dist/commands/daemon/index.js +2 -0
- package/dist/commands/daemon/recover-offline-queue.d.ts +9 -0
- package/dist/commands/daemon/recover-offline-queue.js +24 -0
- package/dist/commands/daemon/start.js +29 -1
- package/dist/commands/instruction-blocks/create.d.ts +19 -0
- package/dist/commands/instruction-blocks/create.js +94 -0
- package/dist/commands/instruction-blocks/delete.d.ts +13 -0
- package/dist/commands/instruction-blocks/delete.js +35 -0
- package/dist/commands/instruction-blocks/list.d.ts +13 -0
- package/dist/commands/instruction-blocks/list.js +71 -0
- package/dist/commands/instruction-blocks/show.d.ts +12 -0
- package/dist/commands/instruction-blocks/show.js +35 -0
- package/dist/commands/instruction-blocks/update.d.ts +23 -0
- package/dist/commands/instruction-blocks/update.js +94 -0
- package/dist/commands/spaces/auto-executor/preview.d.ts +1 -0
- package/dist/commands/spaces/auto-executor/preview.js +62 -3
- package/dist/commands/spaces/auto-executor/update.d.ts +4 -0
- package/dist/commands/spaces/auto-executor/update.js +78 -7
- package/dist/commands/spaces/capacity.js +14 -2
- package/dist/commands/spaces/update.d.ts +4 -0
- package/dist/commands/spaces/update.js +49 -2
- package/dist/commands/tasks/complete.d.ts +12 -5
- package/dist/commands/tasks/complete.js +107 -28
- package/dist/commands/tasks/continue-ready.js +9 -1
- package/dist/commands/tasks/create.d.ts +1 -0
- package/dist/commands/tasks/create.js +27 -2
- package/dist/commands/tasks/deliverable/add.d.ts +23 -0
- package/dist/commands/tasks/deliverable/add.js +70 -0
- package/dist/commands/tasks/deliverable/confirm.d.ts +35 -0
- package/dist/commands/tasks/deliverable/confirm.js +77 -0
- package/dist/commands/tasks/events/append.d.ts +1 -1
- package/dist/commands/tasks/events/append.js +4 -4
- package/dist/commands/tasks/expectations.d.ts +12 -0
- package/dist/commands/tasks/expectations.js +111 -0
- package/dist/commands/tasks/judge.d.ts +29 -0
- package/dist/commands/tasks/judge.js +68 -0
- package/dist/commands/tasks/report.d.ts +1 -1
- package/dist/commands/tasks/report.js +27 -3
- package/dist/commands/tasks/trust.d.ts +35 -0
- package/dist/commands/tasks/trust.js +75 -0
- package/dist/commands/tasks/update.d.ts +1 -0
- package/dist/commands/tasks/update.js +34 -4
- package/dist/commands/tasks/waive.d.ts +25 -0
- package/dist/commands/tasks/waive.js +58 -0
- package/dist/lib/agent-config.js +5 -0
- package/dist/lib/agent-instructions.d.ts +15 -0
- package/dist/lib/agent-instructions.js +55 -0
- package/dist/lib/command-hygiene.d.ts +6 -1
- package/dist/lib/command-hygiene.js +49 -29
- package/dist/lib/commitments.d.ts +28 -3
- package/dist/lib/commitments.js +20 -11
- package/dist/lib/exec-runtime-binary.d.ts +13 -0
- package/dist/lib/exec-runtime-binary.js +45 -0
- package/dist/lib/git-task.d.ts +37 -0
- package/dist/lib/git-task.js +151 -0
- package/dist/lib/instruction-blocks.d.ts +29 -0
- package/dist/lib/instruction-blocks.js +159 -0
- package/dist/lib/runtime-start-verification.d.ts +49 -0
- package/dist/lib/runtime-start-verification.js +108 -0
- package/dist/lib/task-execution.d.ts +18 -2
- package/dist/lib/task-execution.js +19 -14
- package/dist/lib/task-transcript.js +1 -1
- package/dist/lib/tasks.d.ts +50 -0
- package/dist/lib/tasks.js +93 -2
- package/dist/node_modules/@skrr-ai/data-provider/index.js +2393 -2197
- package/dist/node_modules/@skrr-ai/inference-broker/package.json +1 -1
- package/oclif.manifest.json +17725 -15563
- package/package.json +4 -1
|
@@ -2,6 +2,7 @@
|
|
|
2
2
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
3
|
const core_1 = require("@oclif/core");
|
|
4
4
|
const data_provider_1 = require("@skrr-ai/data-provider");
|
|
5
|
+
const tasks_1 = require("../../lib/tasks");
|
|
5
6
|
const base_command_1 = require("../../base-command");
|
|
6
7
|
const web_url_1 = require("../../lib/web-url");
|
|
7
8
|
const prompt_1 = require("../../lib/prompt");
|
|
@@ -70,6 +71,29 @@ class SpacesUpdate extends base_command_1.BaseCommand {
|
|
|
70
71
|
allowNo: true,
|
|
71
72
|
description: 'Whether rollups treat an unestimated task as --default-estimate',
|
|
72
73
|
}),
|
|
74
|
+
'default-expect': core_1.Flags.string({
|
|
75
|
+
multiple: true,
|
|
76
|
+
description: tasks_1.EXPECT_FLAG_HELP +
|
|
77
|
+
' This is the space DEFAULT, and it is where the contract actually gets set: nobody ' +
|
|
78
|
+
'declares one per task, but a space default is configured once and then holds. A ' +
|
|
79
|
+
'per-task --expect wins. Merged into deliveryPolicy without clobbering its other fields.',
|
|
80
|
+
}),
|
|
81
|
+
'delivery-mode': core_1.Flags.string({
|
|
82
|
+
options: ['none', 'branch', 'pr', 'pr_automerge', 'direct'],
|
|
83
|
+
description: "How this space's coding tasks integrate their work. 'pr' opens a pull request; " +
|
|
84
|
+
"'direct' pushes to the integration branch. Note that auto-merging modes are gated " +
|
|
85
|
+
'by --auto-merge-tier: naming one here does not by itself authorise landing code.',
|
|
86
|
+
}),
|
|
87
|
+
'integration-branch': core_1.Flags.string({
|
|
88
|
+
description: "The branch this space's coding tasks integrate into, e.g. main or dev. Also the " +
|
|
89
|
+
'default target for a code expectation that names none.',
|
|
90
|
+
}),
|
|
91
|
+
'auto-merge-tier': core_1.Flags.string({
|
|
92
|
+
options: ['off', 'checks_only', 'trusted'],
|
|
93
|
+
description: 'How far a task may go without a person. "off" (default) downgrades every auto-merging ' +
|
|
94
|
+
'mode to a human-approved PR; "checks_only" honours pr_automerge; "trusted" also honours ' +
|
|
95
|
+
"direct. This is the space owner's sanction and it outranks any per-task override.",
|
|
96
|
+
}),
|
|
73
97
|
'worktree-isolation': core_1.Flags.string({
|
|
74
98
|
options: ['shared', 'ephemeral'],
|
|
75
99
|
description: "Space default for how this space's autonomous task runs use git worktrees. " +
|
|
@@ -105,8 +129,24 @@ class SpacesUpdate extends base_command_1.BaseCommand {
|
|
|
105
129
|
// which the API replaces wholesale on update. Merge into the policy the user
|
|
106
130
|
// supplied via --from-json, else the space's current policy, so setting just
|
|
107
131
|
// the isolation default never clobbers mode / integrationBranch / trust tier.
|
|
132
|
+
// Parse before any write: a bad spec must fail here, not reach the server
|
|
133
|
+
// as a silently-dropped field. A space default nobody is enforcing is worse
|
|
134
|
+
// than none, because every task under it then shows a contract that isn't real.
|
|
135
|
+
let defaultExpectations;
|
|
136
|
+
if (flags['default-expect'] !== undefined && flags['default-expect'].length > 0) {
|
|
137
|
+
const parsed = (0, tasks_1.parseExpectations)(flags['default-expect']);
|
|
138
|
+
if (!parsed.ok) {
|
|
139
|
+
this.error(parsed.error, { exit: 1 });
|
|
140
|
+
}
|
|
141
|
+
else {
|
|
142
|
+
defaultExpectations = parsed.value;
|
|
143
|
+
}
|
|
144
|
+
}
|
|
108
145
|
const isolation = flags['worktree-isolation'];
|
|
109
|
-
|
|
146
|
+
const deliveryMode = flags['delivery-mode'];
|
|
147
|
+
const integrationBranch = flags['integration-branch'];
|
|
148
|
+
const autoMergeTier = flags['auto-merge-tier'];
|
|
149
|
+
if (isolation || defaultExpectations || deliveryMode || integrationBranch || autoMergeTier) {
|
|
110
150
|
let basePolicy = {};
|
|
111
151
|
if (payload.deliveryPolicy && typeof payload.deliveryPolicy === 'object') {
|
|
112
152
|
basePolicy = { ...payload.deliveryPolicy };
|
|
@@ -123,7 +163,14 @@ class SpacesUpdate extends base_command_1.BaseCommand {
|
|
|
123
163
|
// Fall through: send a policy carrying only the isolation default.
|
|
124
164
|
}
|
|
125
165
|
}
|
|
126
|
-
payload.deliveryPolicy = {
|
|
166
|
+
payload.deliveryPolicy = {
|
|
167
|
+
...basePolicy,
|
|
168
|
+
...(isolation ? { executionIsolation: isolation } : {}),
|
|
169
|
+
...(deliveryMode ? { mode: deliveryMode } : {}),
|
|
170
|
+
...(integrationBranch ? { integrationBranch } : {}),
|
|
171
|
+
...(autoMergeTier ? { autoMergeTrustTier: autoMergeTier } : {}),
|
|
172
|
+
...(defaultExpectations ? { defaultExpectations } : {}),
|
|
173
|
+
};
|
|
127
174
|
}
|
|
128
175
|
// Goal links are NOT part of the PATCH body — the server rejects a PATCH
|
|
129
176
|
// /api/spaces/:id carrying `goalIds` with `space_relations_atomic_only` and
|
|
@@ -1,6 +1,7 @@
|
|
|
1
1
|
import { dataService } from '@skrr-ai/data-provider';
|
|
2
2
|
import type { TaskReport } from '@skrr-ai/data-provider';
|
|
3
3
|
import { BaseCommand } from '../../base-command';
|
|
4
|
+
import { type TaskDeliveryReceipt } from '../../lib/git-task';
|
|
4
5
|
type CompletionStatus = 'completed' | 'failed';
|
|
5
6
|
type TaskCommentResult = Awaited<ReturnType<typeof dataService.addTaskComment>>;
|
|
6
7
|
type MovedTaskResult = Awaited<ReturnType<typeof dataService.moveTask>>;
|
|
@@ -9,15 +10,18 @@ export interface CompleteEndpointResponse {
|
|
|
9
10
|
task: MovedTaskResult | null;
|
|
10
11
|
comment: TaskCommentResult | null;
|
|
11
12
|
commentFailed?: boolean;
|
|
13
|
+
deliveryHeld?: boolean;
|
|
14
|
+
deliveryReason?: string;
|
|
12
15
|
}
|
|
13
16
|
export interface CompleteTransport {
|
|
14
17
|
/** POST /api/tasks/:taskId/complete — atomic move + TASK_OUTPUT comment. */
|
|
15
18
|
postComplete: (taskId: string, payload: {
|
|
16
19
|
status: CompletionStatus;
|
|
17
20
|
summary: string;
|
|
18
|
-
|
|
21
|
+
deliverables: string;
|
|
19
22
|
verdict: string;
|
|
20
23
|
report?: TaskReport;
|
|
24
|
+
deliveryReceipt?: TaskDeliveryReceipt;
|
|
21
25
|
}) => Promise<CompleteEndpointResponse>;
|
|
22
26
|
/** Legacy step 1 — POST /api/tasks/:taskId/comments. */
|
|
23
27
|
addComment: (taskId: string, body: string) => Promise<TaskCommentResult>;
|
|
@@ -31,6 +35,8 @@ export interface DurableCompletionResult {
|
|
|
31
35
|
movedTask: MovedTaskResult | null;
|
|
32
36
|
moved: boolean;
|
|
33
37
|
commentFailed: boolean;
|
|
38
|
+
deliveryHeld?: boolean;
|
|
39
|
+
deliveryReason?: string;
|
|
34
40
|
/** true when the atomic /complete endpoint handled the finalization. */
|
|
35
41
|
usedAtomicEndpoint: boolean;
|
|
36
42
|
}
|
|
@@ -60,7 +66,7 @@ export declare function resolveCompletionTarget(taskRef: string, lookup: (taskRe
|
|
|
60
66
|
* The repo contract calls this "the only terminal closeout", and a task has one
|
|
61
67
|
* canonical outcome — but a second run used to succeed silently, post another
|
|
62
68
|
* TASK_OUTPUT block, and leave `tasks output` rendering the throwaway one as
|
|
63
|
-
* authoritative. The real summary and its
|
|
69
|
+
* authoritative. The real summary and its deliverables link were still on the task
|
|
64
70
|
* but no longer canonical, which is the worst of the available failures: no
|
|
65
71
|
* error, no warning, exit 0, and a record that now says something different.
|
|
66
72
|
*
|
|
@@ -120,9 +126,10 @@ export declare function completeTaskDurably(opts: {
|
|
|
120
126
|
status: CompletionStatus;
|
|
121
127
|
body: string;
|
|
122
128
|
summary: string;
|
|
123
|
-
|
|
129
|
+
deliverables: string;
|
|
124
130
|
verdict: string;
|
|
125
131
|
report?: TaskReport;
|
|
132
|
+
deliveryReceipt?: TaskDeliveryReceipt;
|
|
126
133
|
noMove: boolean;
|
|
127
134
|
transport: CompleteTransport;
|
|
128
135
|
}): Promise<DurableCompletionResult>;
|
|
@@ -139,8 +146,8 @@ export default class TasksComplete extends BaseCommand {
|
|
|
139
146
|
amend: import("@oclif/core/lib/interfaces").BooleanFlag<boolean>;
|
|
140
147
|
status: import("@oclif/core/lib/interfaces").OptionFlag<string, import("@oclif/core/lib/interfaces").CustomOptions>;
|
|
141
148
|
summary: import("@oclif/core/lib/interfaces").OptionFlag<string | undefined, import("@oclif/core/lib/interfaces").CustomOptions>;
|
|
142
|
-
|
|
143
|
-
|
|
149
|
+
deliverables: import("@oclif/core/lib/interfaces").OptionFlag<string[], import("@oclif/core/lib/interfaces").CustomOptions>;
|
|
150
|
+
deliverable: import("@oclif/core/lib/interfaces").OptionFlag<string[] | undefined, import("@oclif/core/lib/interfaces").CustomOptions>;
|
|
144
151
|
verdict: import("@oclif/core/lib/interfaces").OptionFlag<string, import("@oclif/core/lib/interfaces").CustomOptions>;
|
|
145
152
|
outcome: import("@oclif/core/lib/interfaces").OptionFlag<string | undefined, import("@oclif/core/lib/interfaces").CustomOptions>;
|
|
146
153
|
finding: import("@oclif/core/lib/interfaces").OptionFlag<string[] | undefined, import("@oclif/core/lib/interfaces").CustomOptions>;
|
|
@@ -21,6 +21,39 @@ const task_instruction_offer_1 = require("../../lib/task-instruction-offer");
|
|
|
21
21
|
const task_transcript_1 = require("../../lib/task-transcript");
|
|
22
22
|
const web_url_1 = require("../../lib/web-url");
|
|
23
23
|
const tasks_1 = require("../../lib/tasks");
|
|
24
|
+
const git_task_1 = require("../../lib/git-task");
|
|
25
|
+
/**
|
|
26
|
+
* Forward any `--deliverable` value that is really a ledger coordinate.
|
|
27
|
+
*
|
|
28
|
+
* FAIL-OPEN, and quietly. This is a convenience bridge on the completion path:
|
|
29
|
+
* the completion itself is the operation the caller asked for, and a ledger
|
|
30
|
+
* write that fails must not take it down. A caller who needed the row and did
|
|
31
|
+
* not get it meets the gate's refusal, which names the command that writes it.
|
|
32
|
+
*
|
|
33
|
+
* `local`, never anything stronger — this is the executor talking about its own
|
|
34
|
+
* work, which is exactly what the authority ladder is built to weigh.
|
|
35
|
+
*/
|
|
36
|
+
async function recordCoordinateDeliverables({ taskId, values, warn, }) {
|
|
37
|
+
const seen = new Set();
|
|
38
|
+
for (const value of values) {
|
|
39
|
+
const raw = (value ?? '').trim();
|
|
40
|
+
if (!raw || raw === 'none' || seen.has(raw)) {
|
|
41
|
+
continue;
|
|
42
|
+
}
|
|
43
|
+
seen.add(raw);
|
|
44
|
+
const parsed = (0, tasks_1.parseDeliverableCoordinate)(raw);
|
|
45
|
+
if (!parsed.ok) {
|
|
46
|
+
continue;
|
|
47
|
+
}
|
|
48
|
+
try {
|
|
49
|
+
await data_provider_1.dataService.addTaskDeliverable(taskId, parsed.value);
|
|
50
|
+
}
|
|
51
|
+
catch {
|
|
52
|
+
warn(`Recorded "${raw}" in the report but not in the deliverables ledger. ` +
|
|
53
|
+
`If the task refuses to close, run \`tasks deliverable add\` for it.`);
|
|
54
|
+
}
|
|
55
|
+
}
|
|
56
|
+
}
|
|
24
57
|
/**
|
|
25
58
|
* Mutation endpoints require the task's canonical UUID even though the CLI
|
|
26
59
|
* accepts human-readable refs and titles. Resolve the display reference before
|
|
@@ -55,7 +88,7 @@ async function resolveCompletionTarget(taskRef, lookup) {
|
|
|
55
88
|
* The repo contract calls this "the only terminal closeout", and a task has one
|
|
56
89
|
* canonical outcome — but a second run used to succeed silently, post another
|
|
57
90
|
* TASK_OUTPUT block, and leave `tasks output` rendering the throwaway one as
|
|
58
|
-
* authoritative. The real summary and its
|
|
91
|
+
* authoritative. The real summary and its deliverables link were still on the task
|
|
59
92
|
* but no longer canonical, which is the worst of the available failures: no
|
|
60
93
|
* error, no warning, exit 0, and a record that now says something different.
|
|
61
94
|
*
|
|
@@ -128,7 +161,7 @@ exports.TaskCompletionMismatchError = TaskCompletionMismatchError;
|
|
|
128
161
|
* error handling.
|
|
129
162
|
*/
|
|
130
163
|
async function completeTaskDurably(opts) {
|
|
131
|
-
const { taskId, status, body, summary,
|
|
164
|
+
const { taskId, status, body, summary, deliverables, verdict, report, deliveryReceipt, noMove, transport, } = opts;
|
|
132
165
|
if (noMove) {
|
|
133
166
|
const comment = await transport.addComment(taskId, body);
|
|
134
167
|
return {
|
|
@@ -143,9 +176,10 @@ async function completeTaskDurably(opts) {
|
|
|
143
176
|
const response = await transport.postComplete(taskId, {
|
|
144
177
|
status,
|
|
145
178
|
summary,
|
|
146
|
-
|
|
179
|
+
deliverables,
|
|
147
180
|
verdict,
|
|
148
181
|
...(report ? { report } : {}),
|
|
182
|
+
...(deliveryReceipt ? { deliveryReceipt } : {}),
|
|
149
183
|
});
|
|
150
184
|
// Reconcile before reporting. On the session-scoped path the server owns
|
|
151
185
|
// the task selection, so a divergence here means the task we were asked to
|
|
@@ -160,6 +194,8 @@ async function completeTaskDurably(opts) {
|
|
|
160
194
|
movedTask: response.task ?? null,
|
|
161
195
|
moved: true,
|
|
162
196
|
commentFailed: response.commentFailed === true,
|
|
197
|
+
...(response.deliveryHeld ? { deliveryHeld: true } : {}),
|
|
198
|
+
...(response.deliveryReason ? { deliveryReason: response.deliveryReason } : {}),
|
|
163
199
|
usedAtomicEndpoint: true,
|
|
164
200
|
};
|
|
165
201
|
}
|
|
@@ -174,7 +210,13 @@ async function completeTaskDurably(opts) {
|
|
|
174
210
|
// behavior (comment first, then move).
|
|
175
211
|
const comment = await transport.addComment(taskId, body);
|
|
176
212
|
const movedTask = await transport.moveTask(taskId, status === 'completed' ? 'done' : 'failed');
|
|
177
|
-
return {
|
|
213
|
+
return {
|
|
214
|
+
comment,
|
|
215
|
+
movedTask,
|
|
216
|
+
moved: true,
|
|
217
|
+
commentFailed: false,
|
|
218
|
+
usedAtomicEndpoint: false,
|
|
219
|
+
};
|
|
178
220
|
}
|
|
179
221
|
class TasksComplete extends base_command_1.BaseCommand {
|
|
180
222
|
static description = 'Post a canonical TASK_OUTPUT comment and finish a task';
|
|
@@ -199,7 +241,7 @@ class TasksComplete extends base_command_1.BaseCommand {
|
|
|
199
241
|
description: 'Task ID or identifier; overrides daemon runtime context',
|
|
200
242
|
}),
|
|
201
243
|
'from-json': core_1.Flags.string({
|
|
202
|
-
description: 'Path to JSON with taskId, status, summary,
|
|
244
|
+
description: 'Path to JSON with taskId, status, summary, deliverables, and verdict; "-" for stdin',
|
|
203
245
|
}),
|
|
204
246
|
amend: core_1.Flags.boolean({
|
|
205
247
|
description: 'Supersede the canonical outcome of an already-completed task. Without it, a differing report on a closed task is refused',
|
|
@@ -210,13 +252,13 @@ class TasksComplete extends base_command_1.BaseCommand {
|
|
|
210
252
|
default: 'completed',
|
|
211
253
|
}),
|
|
212
254
|
summary: core_1.Flags.string({ description: 'Completion summary' }),
|
|
213
|
-
|
|
214
|
-
description: '
|
|
215
|
-
`The whole TASK_OUTPUT block (status + summary +
|
|
255
|
+
deliverables: core_1.Flags.string({
|
|
256
|
+
description: 'Deliverables produced, or "none". Repeatable, and interchangeable with --deliverable. ' +
|
|
257
|
+
`The whole TASK_OUTPUT block (status + summary + deliverables + verdict) must fit in ${data_provider_1.TASK_OUTPUT_BODY_MAX} ` +
|
|
216
258
|
'characters — link to commits, PRs, or file paths rather than pasting content.',
|
|
217
|
-
// Repeatable, even though `--
|
|
218
|
-
// one letter apart and both read as "the
|
|
219
|
-
// one gets typed — and oclif answered that with "Flag --
|
|
259
|
+
// Repeatable, even though `--deliverable` already is. The two spellings sit
|
|
260
|
+
// one letter apart and both read as "the deliverables go here", so the wrong
|
|
261
|
+
// one gets typed — and oclif answered that with "Flag --deliverables can only
|
|
220
262
|
// be specified once", which names the flag you used and not the sibling
|
|
221
263
|
// that does what you wanted. Dogfooded (OSK-266) by losing a completion
|
|
222
264
|
// call to exactly that, then re-running with the two references pre-joined
|
|
@@ -224,9 +266,9 @@ class TasksComplete extends base_command_1.BaseCommand {
|
|
|
224
266
|
multiple: true,
|
|
225
267
|
default: ['none'],
|
|
226
268
|
}),
|
|
227
|
-
|
|
269
|
+
deliverable: core_1.Flags.string({
|
|
228
270
|
description: 'A single artifact reference (repeatable); joined with commas. Prefer this ' +
|
|
229
|
-
'over pre-joining into --
|
|
271
|
+
'over pre-joining into --deliverables.',
|
|
230
272
|
multiple: true,
|
|
231
273
|
}),
|
|
232
274
|
verdict: core_1.Flags.string({ description: 'Final verdict', default: 'approved' }),
|
|
@@ -271,13 +313,13 @@ class TasksComplete extends base_command_1.BaseCommand {
|
|
|
271
313
|
summary: flags.summary,
|
|
272
314
|
// Both spellings, pooled, then normalized to the one-line form the server
|
|
273
315
|
// stores. Pooling rather than letting one win: someone who passes both has
|
|
274
|
-
// named
|
|
316
|
+
// named deliverables in each, and silently dropping half of them is worse
|
|
275
317
|
// than either flag being unavailable.
|
|
276
|
-
|
|
277
|
-
...(flags.
|
|
318
|
+
deliverables: (0, data_provider_1.normalizeTaskOutputDeliverables)([
|
|
319
|
+
...(flags.deliverable ?? []),
|
|
278
320
|
// The literal default is dropped when it is only the default; a user
|
|
279
|
-
// who typed `--
|
|
280
|
-
...(flags.
|
|
321
|
+
// who typed `--deliverables none` alongside a real one meant the real one.
|
|
322
|
+
...(flags.deliverables ?? []).filter((value) => value !== 'none'),
|
|
281
323
|
]) || 'none',
|
|
282
324
|
verdict: flags.verdict,
|
|
283
325
|
});
|
|
@@ -312,14 +354,14 @@ class TasksComplete extends base_command_1.BaseCommand {
|
|
|
312
354
|
}
|
|
313
355
|
let status;
|
|
314
356
|
let summary;
|
|
315
|
-
let
|
|
357
|
+
let deliverables;
|
|
316
358
|
let verdict;
|
|
317
359
|
let report;
|
|
318
360
|
try {
|
|
319
361
|
status = normalizeStatus(input.status);
|
|
320
362
|
summary = requireOutputField(input.summary, 'summary');
|
|
321
|
-
|
|
322
|
-
optionalOutputField((0, data_provider_1.
|
|
363
|
+
deliverables =
|
|
364
|
+
optionalOutputField((0, data_provider_1.normalizeTaskOutputDeliverables)(input.deliverables)) || 'none';
|
|
323
365
|
verdict =
|
|
324
366
|
optionalOutputField(input.verdict) || (status === 'completed' ? 'approved' : 'failed');
|
|
325
367
|
const jsonReport = input.report && typeof input.report === 'object'
|
|
@@ -360,7 +402,7 @@ class TasksComplete extends base_command_1.BaseCommand {
|
|
|
360
402
|
// budget the server enforces. Discovering an undocumented cap by HTTP 400
|
|
361
403
|
// after a run has already finished is the failure being fixed; a caller must
|
|
362
404
|
// be able to see the limit without provoking it.
|
|
363
|
-
const budget = (0, data_provider_1.measureTaskOutputBudget)({ status, summary,
|
|
405
|
+
const budget = (0, data_provider_1.measureTaskOutputBudget)({ status, summary, deliverables, verdict });
|
|
364
406
|
if (!budget.fits) {
|
|
365
407
|
this.failWithCliError({
|
|
366
408
|
message: (0, data_provider_1.describeTaskOutputOverflow)(budget),
|
|
@@ -375,14 +417,21 @@ class TasksComplete extends base_command_1.BaseCommand {
|
|
|
375
417
|
},
|
|
376
418
|
});
|
|
377
419
|
}
|
|
378
|
-
const body = formatTaskOutput({ status, summary,
|
|
420
|
+
const body = formatTaskOutput({ status, summary, deliverables, verdict });
|
|
421
|
+
// Artifact text such as a commit SHA is a delivery claim. The local probe
|
|
422
|
+
// both verifies remote-default ancestry and anchors a commit that has no
|
|
423
|
+
// local branch before the completion request can release this run.
|
|
424
|
+
const deliveryReceipt = status === 'completed' && !flags['no-move']
|
|
425
|
+
? ((0, git_task_1.inspectTaskDeliveryArtifacts)({ deliverables, taskRef: taskIdentity.id }) ?? undefined)
|
|
426
|
+
: undefined;
|
|
379
427
|
// Computed once, OUTSIDE the try, so the original attempt and the queued
|
|
380
428
|
// replay carry the same key. Must match the payload the outbox stores.
|
|
381
429
|
const completionPayload = {
|
|
382
430
|
summary,
|
|
383
|
-
|
|
431
|
+
deliverables,
|
|
384
432
|
verdict,
|
|
385
433
|
...(report ? { report } : {}),
|
|
434
|
+
...(deliveryReceipt ? { deliveryReceipt } : {}),
|
|
386
435
|
noMove: !!flags['no-move'],
|
|
387
436
|
};
|
|
388
437
|
const completionIdempotencyKey = (0, outbox_1.idempotencyKeyFor)('task-complete', taskIdentity.id, JSON.stringify(completionPayload));
|
|
@@ -456,7 +505,7 @@ class TasksComplete extends base_command_1.BaseCommand {
|
|
|
456
505
|
if (decision === 'refuse') {
|
|
457
506
|
this.failWithCliError({
|
|
458
507
|
message: `Task ${completionTaskId} is already ${priorStatus}, and this report differs from its canonical ` +
|
|
459
|
-
`outcome — completing again would silently replace it (
|
|
508
|
+
`outcome — completing again would silently replace it (deliverables included).\n` +
|
|
460
509
|
` → re-run with \`--amend\` to supersede it deliberately, or read what is there now with ` +
|
|
461
510
|
`\`${this.config.bin} tasks output ${completionTaskId}\`.`,
|
|
462
511
|
code: 'TASK_ALREADY_COMPLETED',
|
|
@@ -466,6 +515,31 @@ class TasksComplete extends base_command_1.BaseCommand {
|
|
|
466
515
|
});
|
|
467
516
|
}
|
|
468
517
|
}
|
|
518
|
+
// THE THIRD SPELLING, BRIDGED.
|
|
519
|
+
//
|
|
520
|
+
// `--deliverable` here is PROSE for the TASK_OUTPUT block. The ledger the
|
|
521
|
+
// completion gate reads is written by `tasks deliverable add`, and nothing
|
|
522
|
+
// connected the two — so an agent that ran
|
|
523
|
+
// `tasks complete --deliverable code:abc@origin/main` had, from its own
|
|
524
|
+
// point of view, recorded its deliverable, and was then refused by a gate
|
|
525
|
+
// whose message told it to record its deliverable.
|
|
526
|
+
//
|
|
527
|
+
// Three surfaces in one CLI answer to the word (`tasks deliverable add`,
|
|
528
|
+
// this flag, and `tasks report --deliverable`), and the project's own
|
|
529
|
+
// operating instructions teach the two that do NOT write the ledger. That
|
|
530
|
+
// is a documentation problem only if you believe people read the docs
|
|
531
|
+
// before the error; the cheaper fix is to make the wrong spelling do the
|
|
532
|
+
// right thing when what was typed is unambiguously a coordinate.
|
|
533
|
+
//
|
|
534
|
+
// Deliberately narrow: only values that PARSE as `<kind>:<identity>` are
|
|
535
|
+
// forwarded. A prose reference like `report.pdf` is left exactly as it was —
|
|
536
|
+
// guessing a coordinate out of prose is how the ledger fills with rows
|
|
537
|
+
// nobody can look up again.
|
|
538
|
+
await recordCoordinateDeliverables({
|
|
539
|
+
taskId: completionTaskId,
|
|
540
|
+
values: [...(flags.deliverable ?? []), ...(flags.deliverables ?? [])],
|
|
541
|
+
warn: (message) => this.warn(message),
|
|
542
|
+
});
|
|
469
543
|
let result;
|
|
470
544
|
try {
|
|
471
545
|
result = await completeTaskDurably({
|
|
@@ -473,9 +547,10 @@ class TasksComplete extends base_command_1.BaseCommand {
|
|
|
473
547
|
status,
|
|
474
548
|
body,
|
|
475
549
|
summary,
|
|
476
|
-
|
|
550
|
+
deliverables,
|
|
477
551
|
verdict,
|
|
478
552
|
report,
|
|
553
|
+
deliveryReceipt,
|
|
479
554
|
noMove: !!flags['no-move'],
|
|
480
555
|
transport: {
|
|
481
556
|
postComplete: (taskId, payload) => (0, api_fetch_1.apiFetch)(
|
|
@@ -496,7 +571,7 @@ class TasksComplete extends base_command_1.BaseCommand {
|
|
|
496
571
|
});
|
|
497
572
|
}
|
|
498
573
|
catch (err) {
|
|
499
|
-
// The completion summary/
|
|
574
|
+
// The completion summary/deliverables/verdict are authored content — the
|
|
500
575
|
// record of what the agent actually did. Losing that to a 503 costs the
|
|
501
576
|
// reasoning, not just a retry, so an OUTAGE queues it durably (OSK-2806)
|
|
502
577
|
// while every 4xx still fails immediately through handleApiError.
|
|
@@ -531,11 +606,12 @@ class TasksComplete extends base_command_1.BaseCommand {
|
|
|
531
606
|
taskOutput: {
|
|
532
607
|
status,
|
|
533
608
|
summary,
|
|
534
|
-
|
|
609
|
+
deliverables,
|
|
535
610
|
verdict,
|
|
536
611
|
...(report ? { report } : {}),
|
|
537
612
|
body,
|
|
538
613
|
},
|
|
614
|
+
...(result.deliveryHeld ? { deliveryHeld: true, deliveryReason: result.deliveryReason } : {}),
|
|
539
615
|
};
|
|
540
616
|
if (flags.json) {
|
|
541
617
|
const url = movedTask
|
|
@@ -558,6 +634,9 @@ class TasksComplete extends base_command_1.BaseCommand {
|
|
|
558
634
|
? `Task ${completedTaskId} was already ${newStatus}; the outcome was amended.`
|
|
559
635
|
: `Moved task ${completedTaskId} to ${newStatus}`);
|
|
560
636
|
}
|
|
637
|
+
if (result.deliveryHeld) {
|
|
638
|
+
this.warn('Delivery is not verified on the default branch; the task was held at in_review instead of done.');
|
|
639
|
+
}
|
|
561
640
|
if (report) {
|
|
562
641
|
const url = (0, web_url_1.taskUrlFrom)(this.cliConfig.baseURL, movedTask ?? { id: completedTaskId });
|
|
563
642
|
if (url)
|
|
@@ -70,7 +70,15 @@ class TasksContinueReady extends base_command_1.BaseCommand {
|
|
|
70
70
|
this.handleApiError(err);
|
|
71
71
|
}
|
|
72
72
|
}
|
|
73
|
-
|
|
73
|
+
// `availableSlots` is null for an UNLIMITED space, which is the default.
|
|
74
|
+
// `Math.min(n, null)` is 0, so the previous arithmetic silently computed a
|
|
75
|
+
// start limit of zero and reported every ready task as "skipped for
|
|
76
|
+
// capacity" on exactly the spaces that had no capacity limit at all. The
|
|
77
|
+
// platform's own helper warns about this: callers must branch on null
|
|
78
|
+
// rather than do arithmetic on it (SpaceTaskConcurrency.availableSlotsFor).
|
|
79
|
+
const freeSlots = capacity && capacity.availableSlots !== null
|
|
80
|
+
? capacity.availableSlots
|
|
81
|
+
: Number.POSITIVE_INFINITY;
|
|
74
82
|
const startLimit = Math.max(0, Math.min(flags['max-starts'], freeSlots));
|
|
75
83
|
const selected = ready.slice(0, startLimit);
|
|
76
84
|
const skippedForCapacity = ready.slice(startLimit);
|
|
@@ -53,6 +53,7 @@ export default class TasksCreate extends BaseCommand {
|
|
|
53
53
|
'assignee-user': import("@oclif/core/lib/interfaces").OptionFlag<string[] | undefined, import("@oclif/core/lib/interfaces").CustomOptions>;
|
|
54
54
|
'dri-agent': import("@oclif/core/lib/interfaces").OptionFlag<string | undefined, import("@oclif/core/lib/interfaces").CustomOptions>;
|
|
55
55
|
'dri-user': import("@oclif/core/lib/interfaces").OptionFlag<string | undefined, import("@oclif/core/lib/interfaces").CustomOptions>;
|
|
56
|
+
expect: import("@oclif/core/lib/interfaces").OptionFlag<string[] | undefined, import("@oclif/core/lib/interfaces").CustomOptions>;
|
|
56
57
|
'execution-mode': import("@oclif/core/lib/interfaces").OptionFlag<string | undefined, import("@oclif/core/lib/interfaces").CustomOptions>;
|
|
57
58
|
'start-date': import("@oclif/core/lib/interfaces").OptionFlag<string | undefined, import("@oclif/core/lib/interfaces").CustomOptions>;
|
|
58
59
|
'due-date': import("@oclif/core/lib/interfaces").OptionFlag<string | undefined, import("@oclif/core/lib/interfaces").CustomOptions>;
|
|
@@ -71,7 +71,8 @@ class TasksCreate extends base_command_1.BaseCommand {
|
|
|
71
71
|
description: 'Validate and print the create payload without writing a task',
|
|
72
72
|
}),
|
|
73
73
|
'from-json': core_1.Flags.string({
|
|
74
|
-
description: 'Path to a JSON file (or "-" for stdin) to use as the request body'
|
|
74
|
+
description: 'Path to a JSON file (or "-" for stdin) to use as the request body. ' +
|
|
75
|
+
'Accepts stored names (projectId, goalId) and create-flag aliases (project, goal, spawnedBy).',
|
|
75
76
|
}),
|
|
76
77
|
template: core_1.Flags.string({
|
|
77
78
|
description: 'Saved task template ID, or an exact template name with --space. Creates its whole task tree; supplied task flags override the root.',
|
|
@@ -136,6 +137,12 @@ class TasksCreate extends base_command_1.BaseCommand {
|
|
|
136
137
|
'dri-user': core_1.Flags.string({
|
|
137
138
|
description: 'Explicit accountable human DRI (separate from assignees)',
|
|
138
139
|
}),
|
|
140
|
+
expect: core_1.Flags.string({
|
|
141
|
+
multiple: true,
|
|
142
|
+
description: tasks_1.EXPECT_FLAG_HELP +
|
|
143
|
+
" Injected into the agent's instructions and checked against what the run produced. " +
|
|
144
|
+
'Omit to inherit the space default.',
|
|
145
|
+
}),
|
|
139
146
|
'execution-mode': core_1.Flags.string({
|
|
140
147
|
description: 'Who executes the task: agent or human. Required when both Humans and Agents are ' +
|
|
141
148
|
'assigned. Passing "human" with no assignee flags files the task against you, ' +
|
|
@@ -184,7 +191,25 @@ class TasksCreate extends base_command_1.BaseCommand {
|
|
|
184
191
|
retryable: false,
|
|
185
192
|
});
|
|
186
193
|
}
|
|
187
|
-
|
|
194
|
+
// Resolve --expect BEFORE any write. A bad spec must fail here rather than
|
|
195
|
+
// reaching the server as a silently-dropped field: a declaration that did
|
|
196
|
+
// not stick is worse than none, because the board then shows a contract
|
|
197
|
+
// nobody is enforcing.
|
|
198
|
+
const parsedExpectations = (0, tasks_1.parseExpectations)(flags.expect);
|
|
199
|
+
if (!parsedExpectations.ok) {
|
|
200
|
+
this.failWithCliError({
|
|
201
|
+
message: parsedExpectations.error,
|
|
202
|
+
code: 'INVALID_TASK_EXPECTATION',
|
|
203
|
+
exit: 1,
|
|
204
|
+
retryable: false,
|
|
205
|
+
});
|
|
206
|
+
}
|
|
207
|
+
const payload = (0, tasks_1.buildTaskCreatePayload)(body, {
|
|
208
|
+
...flags,
|
|
209
|
+
expectations: parsedExpectations.ok && parsedExpectations.value.length > 0
|
|
210
|
+
? parsedExpectations.value
|
|
211
|
+
: undefined,
|
|
212
|
+
});
|
|
188
213
|
const isTemplateInstantiation = typeof payload.templateId === 'string' && payload.templateId.length > 0;
|
|
189
214
|
// Template defaults (including its assigned agents) are part of the saved
|
|
190
215
|
// resource. Do not let the ordinary "who owns an empty task?" policy
|
|
@@ -0,0 +1,23 @@
|
|
|
1
|
+
import { BaseCommand } from '../../../base-command';
|
|
2
|
+
/**
|
|
3
|
+
* `skrr tasks deliverable add <task> <kind>:<identity>[@<location>]`
|
|
4
|
+
*
|
|
5
|
+
* The command an agent runs to say what it produced. Deliberately small: one
|
|
6
|
+
* positional carrying a coordinate it already knows, because the measured
|
|
7
|
+
* behaviour is that agents emit a real commit sha 77% of the time when asked
|
|
8
|
+
* for it in a required field, and 0% of the time when asked for it in an
|
|
9
|
+
* optional structured one.
|
|
10
|
+
*/
|
|
11
|
+
export default class TasksDeliverableAdd extends BaseCommand {
|
|
12
|
+
static description: string;
|
|
13
|
+
static examples: string[];
|
|
14
|
+
static args: {
|
|
15
|
+
task: import("@oclif/core/lib/interfaces").Arg<string, Record<string, unknown>>;
|
|
16
|
+
coordinate: import("@oclif/core/lib/interfaces").Arg<string, Record<string, unknown>>;
|
|
17
|
+
};
|
|
18
|
+
static flags: {
|
|
19
|
+
lifecycle: import("@oclif/core/lib/interfaces").OptionFlag<string | undefined, import("@oclif/core/lib/interfaces").CustomOptions>;
|
|
20
|
+
json: import("@oclif/core/lib/interfaces").BooleanFlag<boolean>;
|
|
21
|
+
};
|
|
22
|
+
run(): Promise<void>;
|
|
23
|
+
}
|
|
@@ -0,0 +1,70 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
const core_1 = require("@oclif/core");
|
|
4
|
+
const data_provider_1 = require("@skrr-ai/data-provider");
|
|
5
|
+
const base_command_1 = require("../../../base-command");
|
|
6
|
+
const tasks_1 = require("../../../lib/tasks");
|
|
7
|
+
/**
|
|
8
|
+
* `skrr tasks deliverable add <task> <kind>:<identity>[@<location>]`
|
|
9
|
+
*
|
|
10
|
+
* The command an agent runs to say what it produced. Deliberately small: one
|
|
11
|
+
* positional carrying a coordinate it already knows, because the measured
|
|
12
|
+
* behaviour is that agents emit a real commit sha 77% of the time when asked
|
|
13
|
+
* for it in a required field, and 0% of the time when asked for it in an
|
|
14
|
+
* optional structured one.
|
|
15
|
+
*/
|
|
16
|
+
class TasksDeliverableAdd extends base_command_1.BaseCommand {
|
|
17
|
+
static description = 'Record something this task produced (a commit, a page, a URL, a file, a message, a dataset)';
|
|
18
|
+
static examples = [
|
|
19
|
+
'<%= config.bin %> tasks deliverable add <task-id> code:a1b2c3d@origin/main',
|
|
20
|
+
'<%= config.bin %> tasks deliverable add <task-id> page:d_92@Engineering/deploy',
|
|
21
|
+
'<%= config.bin %> tasks deliverable add <task-id> file/pdf:f_71@Drive/Reports',
|
|
22
|
+
'<%= config.bin %> tasks deliverable add <task-id> code:a1b2c3d --lifecycle in_review',
|
|
23
|
+
];
|
|
24
|
+
static args = {
|
|
25
|
+
task: core_1.Args.string({ description: 'Task id or ref', required: true, ignoreStdin: true }),
|
|
26
|
+
coordinate: core_1.Args.string({
|
|
27
|
+
description: '<kind>[/<format>]:<identity>[@<location>] — e.g. code:a1b2c3d@origin/main, ' +
|
|
28
|
+
'file/pdf:report_92@Drive/Reports',
|
|
29
|
+
required: true,
|
|
30
|
+
ignoreStdin: true,
|
|
31
|
+
}),
|
|
32
|
+
};
|
|
33
|
+
static flags = {
|
|
34
|
+
lifecycle: core_1.Flags.string({
|
|
35
|
+
options: ['local', 'in_review', 'durable', 'blocked', 'superseded'],
|
|
36
|
+
description: 'How far along it is. Defaults to `local` — recording that a commit exists is not a ' +
|
|
37
|
+
'claim that it landed. Only an external receipt should assert `durable`.',
|
|
38
|
+
}),
|
|
39
|
+
json: core_1.Flags.boolean({ description: 'Output as JSON' }),
|
|
40
|
+
};
|
|
41
|
+
async run() {
|
|
42
|
+
this.requireAuth();
|
|
43
|
+
const { args, flags } = await this.parse(TasksDeliverableAdd);
|
|
44
|
+
const parsed = (0, tasks_1.parseDeliverableCoordinate)(args.coordinate);
|
|
45
|
+
if (!parsed.ok) {
|
|
46
|
+
// For an agent this message IS the documentation — it hits the error and
|
|
47
|
+
// corrects, so the refusal carries the whole form and vocabulary.
|
|
48
|
+
this.error(parsed.error, { exit: 1 });
|
|
49
|
+
return;
|
|
50
|
+
}
|
|
51
|
+
let result;
|
|
52
|
+
try {
|
|
53
|
+
result = await data_provider_1.dataService.addTaskDeliverable(args.task, {
|
|
54
|
+
...parsed.value,
|
|
55
|
+
...(flags.lifecycle ? { lifecycle: flags.lifecycle } : {}),
|
|
56
|
+
});
|
|
57
|
+
}
|
|
58
|
+
catch (err) {
|
|
59
|
+
this.handleApiError(err);
|
|
60
|
+
return;
|
|
61
|
+
}
|
|
62
|
+
if (flags.json) {
|
|
63
|
+
this.log(JSON.stringify(result, null, 2));
|
|
64
|
+
return;
|
|
65
|
+
}
|
|
66
|
+
const where = parsed.value.location ? ` at ${parsed.value.location}` : '';
|
|
67
|
+
this.log(`Recorded ${parsed.value.kind}:${parsed.value.identity}${where} (${flags.lifecycle || 'local'}).`);
|
|
68
|
+
}
|
|
69
|
+
}
|
|
70
|
+
exports.default = TasksDeliverableAdd;
|
|
@@ -0,0 +1,35 @@
|
|
|
1
|
+
import { BaseCommand } from '../../../base-command';
|
|
2
|
+
/**
|
|
3
|
+
* `skrr tasks deliverable confirm <task> <kind>:<identity>[@<location>] --reason "<what you checked>"`
|
|
4
|
+
*
|
|
5
|
+
* Somebody looked, and it is there.
|
|
6
|
+
*
|
|
7
|
+
* WHY THIS IS A SEPARATE COMMAND from `deliverable add`. Recording and vouching
|
|
8
|
+
* are different claims. `add` says "this exists, and I am the one who made it";
|
|
9
|
+
* this says "I checked, independently of whoever made it". Five of the six
|
|
10
|
+
* outcome kinds have no automatic observer — nothing in the platform watches a
|
|
11
|
+
* Drive folder or a Slack channel — so without this rung an expectation for a
|
|
12
|
+
* PDF or a posted message could only ever be closed by waiving it, which
|
|
13
|
+
* records "we decided we did not need this" about work that was actually done.
|
|
14
|
+
*
|
|
15
|
+
* The reason is required, for the same purpose a waiver's and a judgement's
|
|
16
|
+
* are: the entire value of a confirmation is that a person actually looked, and
|
|
17
|
+
* a confirmation with nothing written down is indistinguishable from a click.
|
|
18
|
+
*
|
|
19
|
+
* It UPSERTS. Confirming something the executor never recorded is legitimate —
|
|
20
|
+
* the thing is there either way — and making a person file the row first before
|
|
21
|
+
* they may vouch for it is ceremony that buys nothing.
|
|
22
|
+
*/
|
|
23
|
+
export default class TasksDeliverableConfirm extends BaseCommand {
|
|
24
|
+
static description: string;
|
|
25
|
+
static examples: string[];
|
|
26
|
+
static args: {
|
|
27
|
+
task: import("@oclif/core/lib/interfaces").Arg<string, Record<string, unknown>>;
|
|
28
|
+
coordinate: import("@oclif/core/lib/interfaces").Arg<string, Record<string, unknown>>;
|
|
29
|
+
};
|
|
30
|
+
static flags: {
|
|
31
|
+
reason: import("@oclif/core/lib/interfaces").OptionFlag<string, import("@oclif/core/lib/interfaces").CustomOptions>;
|
|
32
|
+
json: import("@oclif/core/lib/interfaces").BooleanFlag<boolean>;
|
|
33
|
+
};
|
|
34
|
+
run(): Promise<void>;
|
|
35
|
+
}
|