@deftai/directive 0.119.13 → 0.121.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.
- package/dist/authz.js +41 -7
- package/dist/changelog-check.js +14 -6
- package/dist/dispatch.d.ts +2 -2
- package/dist/dispatch.js +17 -0
- package/dist/hook-dispatch.js +4 -0
- package/dist/pre-pr-run.d.ts +39 -0
- package/dist/pre-pr-run.js +514 -0
- package/dist/render-cli/spec-reconstruct-cli.d.ts +3 -0
- package/dist/render-cli/spec-reconstruct-cli.js +15 -0
- package/dist/subagent-pre-cancel.d.ts +21 -0
- package/dist/subagent-pre-cancel.js +240 -0
- package/dist/subagent-steer-write.d.ts +41 -0
- package/dist/subagent-steer-write.js +333 -0
- package/dist/verify-cohort-review-monitors.d.ts +5 -0
- package/dist/verify-cohort-review-monitors.js +11 -0
- package/dist/verify-completed-tracked.d.ts +4 -1
- package/dist/verify-completed-tracked.js +60 -9
- package/dist/verify-review-monitor.d.ts +2 -0
- package/dist/verify-review-monitor.js +66 -1
- package/dist/verify-source-cli/verify-spec-drift.d.ts +3 -0
- package/dist/verify-source-cli/verify-spec-drift.js +15 -0
- package/dist/verify-story-ready.js +5 -2
- package/dist/verify-subagent-alive.js +17 -1
- package/package.json +3 -3
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
#!/usr/bin/env node
|
|
2
2
|
import { resolve } from "node:path";
|
|
3
3
|
import { fileURLToPath } from "node:url";
|
|
4
|
-
import { COMPLETED_TRACKED_PROGRESS_MIN_BLOBS, COMPLETED_TRACKED_PROGRESS_THRESHOLD_MS, evaluateCompletedTracked, shouldAnnounceProgress, shouldAnnounceUpFrontCount, } from "@deftai/directive-core/lifecycle";
|
|
4
|
+
import { COMPLETED_TRACKED_PROGRESS_MIN_BLOBS, COMPLETED_TRACKED_PROGRESS_THRESHOLD_MS, evaluateCompletedTracked, evaluateShippedClosedDiscovery, shouldAnnounceProgress, shouldAnnounceUpFrontCount, } from "@deftai/directive-core/lifecycle";
|
|
5
5
|
function parseIssueNumber(raw) {
|
|
6
6
|
const trimmed = raw.startsWith("#") ? raw.slice(1) : raw;
|
|
7
7
|
if (!/^\d+$/.test(trimmed)) {
|
|
@@ -10,7 +10,7 @@ function parseIssueNumber(raw) {
|
|
|
10
10
|
const value = Number(trimmed);
|
|
11
11
|
return Number.isInteger(value) && value > 0 ? value : null;
|
|
12
12
|
}
|
|
13
|
-
/** Parse verify-completed-tracked CLI args (#3264 / #3476). */
|
|
13
|
+
/** Parse verify-completed-tracked CLI args (#3264 / #3476 / #3495). */
|
|
14
14
|
export function parseArgs(argv) {
|
|
15
15
|
const parsed = {
|
|
16
16
|
projectRoot: ".",
|
|
@@ -19,6 +19,8 @@ export function parseArgs(argv) {
|
|
|
19
19
|
issue: null,
|
|
20
20
|
quiet: false,
|
|
21
21
|
skipGh: false,
|
|
22
|
+
discover: null,
|
|
23
|
+
enforce: false,
|
|
22
24
|
};
|
|
23
25
|
for (let i = 0; i < argv.length; i += 1) {
|
|
24
26
|
const arg = argv[i];
|
|
@@ -28,6 +30,15 @@ export function parseArgs(argv) {
|
|
|
28
30
|
else if (arg === "--skip-gh") {
|
|
29
31
|
parsed.skipGh = true;
|
|
30
32
|
}
|
|
33
|
+
else if (arg === "--discover") {
|
|
34
|
+
parsed.discover = true;
|
|
35
|
+
}
|
|
36
|
+
else if (arg === "--no-discover") {
|
|
37
|
+
parsed.discover = false;
|
|
38
|
+
}
|
|
39
|
+
else if (arg === "--enforce") {
|
|
40
|
+
parsed.enforce = true;
|
|
41
|
+
}
|
|
31
42
|
else if (arg === "--project-root") {
|
|
32
43
|
const value = argv[i + 1];
|
|
33
44
|
if (value === undefined) {
|
|
@@ -87,6 +98,16 @@ export function parseArgs(argv) {
|
|
|
87
98
|
}
|
|
88
99
|
return parsed;
|
|
89
100
|
}
|
|
101
|
+
function shouldRunDiscovery(args) {
|
|
102
|
+
if (args.discover === false) {
|
|
103
|
+
return false;
|
|
104
|
+
}
|
|
105
|
+
if (args.discover === true || args.enforce) {
|
|
106
|
+
return true;
|
|
107
|
+
}
|
|
108
|
+
// Warn-first default: unscoped corpus scans discover; --issue N stays land-only.
|
|
109
|
+
return args.issue === null;
|
|
110
|
+
}
|
|
90
111
|
/** Run the gate and return the process exit code. */
|
|
91
112
|
export function run(argv) {
|
|
92
113
|
const args = parseArgs(argv);
|
|
@@ -97,7 +118,7 @@ export function run(argv) {
|
|
|
97
118
|
const projectRoot = resolve(args.projectRoot);
|
|
98
119
|
const thresholdMs = resolveProgressThresholdMs();
|
|
99
120
|
let announced = false;
|
|
100
|
-
const
|
|
121
|
+
const land = evaluateCompletedTracked(projectRoot, {
|
|
101
122
|
quiet: args.quiet,
|
|
102
123
|
repo: args.repo,
|
|
103
124
|
tip: args.tip,
|
|
@@ -117,15 +138,45 @@ export function run(argv) {
|
|
|
117
138
|
writeProgress(event);
|
|
118
139
|
},
|
|
119
140
|
});
|
|
120
|
-
|
|
121
|
-
|
|
122
|
-
|
|
141
|
+
let discoveryCode = 0;
|
|
142
|
+
let discoveryMessage = "";
|
|
143
|
+
let discoveryStream = "none";
|
|
144
|
+
if (shouldRunDiscovery(args)) {
|
|
145
|
+
const discovery = evaluateShippedClosedDiscovery(projectRoot, {
|
|
146
|
+
quiet: args.quiet,
|
|
147
|
+
repo: args.repo,
|
|
148
|
+
tip: args.tip,
|
|
149
|
+
skipGh: args.skipGh,
|
|
150
|
+
enforce: args.enforce,
|
|
151
|
+
});
|
|
152
|
+
discoveryCode = discovery.code;
|
|
153
|
+
discoveryMessage = discovery.message;
|
|
154
|
+
discoveryStream = discovery.stream;
|
|
155
|
+
}
|
|
156
|
+
// Scoped land fail-closed must not be softened by discovery warn (#3495 Reject).
|
|
157
|
+
if (land.message.length > 0) {
|
|
158
|
+
if (land.stream === "stdout") {
|
|
159
|
+
process.stdout.write(`${land.message}\n`);
|
|
123
160
|
}
|
|
124
|
-
else if (
|
|
125
|
-
process.stderr.write(`${
|
|
161
|
+
else if (land.stream === "stderr") {
|
|
162
|
+
process.stderr.write(`${land.message}\n`);
|
|
126
163
|
}
|
|
127
164
|
}
|
|
128
|
-
|
|
165
|
+
if (discoveryMessage.length > 0) {
|
|
166
|
+
if (discoveryStream === "stdout") {
|
|
167
|
+
process.stdout.write(`${discoveryMessage}\n`);
|
|
168
|
+
}
|
|
169
|
+
else if (discoveryStream === "stderr") {
|
|
170
|
+
process.stderr.write(`${discoveryMessage}\n`);
|
|
171
|
+
}
|
|
172
|
+
}
|
|
173
|
+
if (land.code === 2 || discoveryCode === 2) {
|
|
174
|
+
return 2;
|
|
175
|
+
}
|
|
176
|
+
if (land.code === 1 || discoveryCode === 1) {
|
|
177
|
+
return 1;
|
|
178
|
+
}
|
|
179
|
+
return 0;
|
|
129
180
|
}
|
|
130
181
|
function resolveProgressThresholdMs() {
|
|
131
182
|
const raw = process.env.DEFT_COMPLETED_TRACKED_PROGRESS_MS;
|
|
@@ -14,6 +14,8 @@ interface ParsedArgs {
|
|
|
14
14
|
liveWait: boolean;
|
|
15
15
|
explicitFinish: boolean;
|
|
16
16
|
stickyLease: boolean;
|
|
17
|
+
/** DeliveryAttemptRecord.startedAt for Approach 1 arm-startup halt (#5278 P3). */
|
|
18
|
+
dispatchStartedAt: string | null;
|
|
17
19
|
help: boolean;
|
|
18
20
|
error?: string;
|
|
19
21
|
}
|
|
@@ -2,7 +2,9 @@
|
|
|
2
2
|
import { resolve } from "node:path";
|
|
3
3
|
import { fileURLToPath } from "node:url";
|
|
4
4
|
import { bindLivePhaseCorrectWait, evaluateMergePathArm, } from "@deftai/directive-core/dist/pr-watch/main.js";
|
|
5
|
+
import { evaluateApproach1ArmStartup } from "@deftai/directive-core/orchestration";
|
|
5
6
|
import { EXIT_CONFIG_ERROR, EXIT_NOT_READY, evaluateReviewMonitorGate, formatApproach1BabysitterOneLiner, isTier1, REVIEW_MONITOR_HELP, spawnRedirect, verifyResultToJson, } from "@deftai/directive-core/review-monitor";
|
|
7
|
+
import { writeMergePathExplicitFinishAttestation } from "@deftai/directive-core/swarm";
|
|
6
8
|
const CALL_SITES = new Set([
|
|
7
9
|
"solo",
|
|
8
10
|
"swarm-phase5-6",
|
|
@@ -23,6 +25,7 @@ export function parseVerifyReviewMonitorArgs(argv) {
|
|
|
23
25
|
liveWait: false,
|
|
24
26
|
explicitFinish: false,
|
|
25
27
|
stickyLease: false,
|
|
28
|
+
dispatchStartedAt: null,
|
|
26
29
|
help: false,
|
|
27
30
|
};
|
|
28
31
|
for (let i = 0; i < argv.length; i += 1) {
|
|
@@ -45,6 +48,17 @@ export function parseVerifyReviewMonitorArgs(argv) {
|
|
|
45
48
|
else if (arg === "--sticky-lease") {
|
|
46
49
|
acc.stickyLease = true;
|
|
47
50
|
}
|
|
51
|
+
else if (arg === "--dispatch-started-at") {
|
|
52
|
+
const value = argv[i + 1];
|
|
53
|
+
if (value === undefined) {
|
|
54
|
+
return { ...acc, error: "argument --dispatch-started-at: expected one argument" };
|
|
55
|
+
}
|
|
56
|
+
acc.dispatchStartedAt = value;
|
|
57
|
+
i += 1;
|
|
58
|
+
}
|
|
59
|
+
else if (arg?.startsWith("--dispatch-started-at=")) {
|
|
60
|
+
acc.dispatchStartedAt = arg.slice("--dispatch-started-at=".length);
|
|
61
|
+
}
|
|
48
62
|
else if (arg === "--approach3") {
|
|
49
63
|
acc.approach3 = true;
|
|
50
64
|
}
|
|
@@ -146,6 +160,9 @@ export function run(argv) {
|
|
|
146
160
|
" parent-shell pr:watch (parent_id=pr-watch) does not arm.\n" +
|
|
147
161
|
" --explicit-finish Attest option-C BLOCKED/FAILED finish for this PR\n" +
|
|
148
162
|
" --sticky-lease Attest a fresh sticky lease (not sufficient alone)\n" +
|
|
163
|
+
" --dispatch-started-at DeliveryAttemptRecord.startedAt ISO for Approach 1\n" +
|
|
164
|
+
" arm-startup halt (#5278); when merge-path-arm stays\n" +
|
|
165
|
+
" red past the 3m allowance, emit approach1-arm-startup.\n" +
|
|
149
166
|
" Prefer Approach 1 / native pr:watch --monitor-agent-id <id> or post-CLEAN\n" +
|
|
150
167
|
" pr:wait-mergeable-and-merge; homemade line-parsed --json is not an arm.\n");
|
|
151
168
|
return 0;
|
|
@@ -174,6 +191,26 @@ export function run(argv) {
|
|
|
174
191
|
});
|
|
175
192
|
let arm = null;
|
|
176
193
|
if (args.mergePathArm) {
|
|
194
|
+
// Durable option-C attestation for cohort inventory halted-explicit (#5318).
|
|
195
|
+
// Do not write (or arm from) --explicit-finish when the monitor gate is a config error,
|
|
196
|
+
// and fail closed if the durable write itself fails.
|
|
197
|
+
let explicitFinishDurable = false;
|
|
198
|
+
if (args.explicitFinish) {
|
|
199
|
+
if (result.exitCode === EXIT_CONFIG_ERROR) {
|
|
200
|
+
explicitFinishDurable = false;
|
|
201
|
+
}
|
|
202
|
+
else {
|
|
203
|
+
const written = writeMergePathExplicitFinishAttestation(resolve(args.projectRoot), args.pr, {
|
|
204
|
+
source: "verify:review-monitor --explicit-finish",
|
|
205
|
+
});
|
|
206
|
+
if (!written.ok) {
|
|
207
|
+
process.stderr.write(`verify_review_monitor: ${written.reason}
|
|
208
|
+
`);
|
|
209
|
+
return EXIT_CONFIG_ERROR;
|
|
210
|
+
}
|
|
211
|
+
explicitFinishDurable = true;
|
|
212
|
+
}
|
|
213
|
+
}
|
|
177
214
|
// Bind --live-wait to lease (#5018) + process-liveness heartbeat (#5020) on Tier 1.
|
|
178
215
|
// spawn_subagent identity join is already applied in evaluateReviewMonitorGate (#5219).
|
|
179
216
|
const leaseEvidence = result.monitorRecord !== null;
|
|
@@ -186,7 +223,7 @@ export function run(argv) {
|
|
|
186
223
|
});
|
|
187
224
|
arm = evaluateMergePathArm({
|
|
188
225
|
livePhaseCorrectWait: liveBind.livePhaseCorrectWait,
|
|
189
|
-
explicitFinish:
|
|
226
|
+
explicitFinish: explicitFinishDurable,
|
|
190
227
|
stickyLeaseActive: args.stickyLease || leaseEvidence,
|
|
191
228
|
});
|
|
192
229
|
if (args.liveWait && !liveBind.livePhaseCorrectWait && !args.explicitFinish && !arm.armed) {
|
|
@@ -224,6 +261,26 @@ export function run(argv) {
|
|
|
224
261
|
};
|
|
225
262
|
}
|
|
226
263
|
}
|
|
264
|
+
// Approach 1 arm-startup halt (#5278 P3): production caller for evaluateApproach1ArmStartup.
|
|
265
|
+
let armStartupHalt = null;
|
|
266
|
+
if (args.mergePathArm &&
|
|
267
|
+
args.dispatchStartedAt !== null &&
|
|
268
|
+
args.dispatchStartedAt.trim().length > 0 &&
|
|
269
|
+
arm !== null &&
|
|
270
|
+
!arm.armed &&
|
|
271
|
+
!args.explicitFinish) {
|
|
272
|
+
armStartupHalt = evaluateApproach1ArmStartup({
|
|
273
|
+
dispatchStartedAt: args.dispatchStartedAt,
|
|
274
|
+
probeReady: false,
|
|
275
|
+
});
|
|
276
|
+
if (armStartupHalt.halt && armStartupHalt.message.length > 0) {
|
|
277
|
+
arm = {
|
|
278
|
+
armed: false,
|
|
279
|
+
reason: "unarmed_stand_down",
|
|
280
|
+
message: `${arm.message}\n${armStartupHalt.message}`,
|
|
281
|
+
};
|
|
282
|
+
}
|
|
283
|
+
}
|
|
227
284
|
if (args.emitJson) {
|
|
228
285
|
const payload = verifyResultToJson(result);
|
|
229
286
|
if (arm !== null) {
|
|
@@ -237,6 +294,14 @@ export function run(argv) {
|
|
|
237
294
|
lease_evidence: result.monitorRecord !== null,
|
|
238
295
|
heartbeat_active: result.heartbeatActive,
|
|
239
296
|
};
|
|
297
|
+
if (armStartupHalt !== null) {
|
|
298
|
+
payload.approach1_arm_startup = {
|
|
299
|
+
halt: armStartupHalt.halt,
|
|
300
|
+
halt_class: armStartupHalt.halt_class,
|
|
301
|
+
elapsed_seconds: armStartupHalt.elapsed_seconds,
|
|
302
|
+
message: armStartupHalt.message,
|
|
303
|
+
};
|
|
304
|
+
}
|
|
240
305
|
// Combined gate+arm: unarmed fails closed even when the monitor gate is ready.
|
|
241
306
|
if (!arm.armed && result.exitCode !== EXIT_CONFIG_ERROR) {
|
|
242
307
|
payload.ready = false;
|
|
@@ -0,0 +1,15 @@
|
|
|
1
|
+
#!/usr/bin/env node
|
|
2
|
+
import { fileURLToPath } from "node:url";
|
|
3
|
+
import { runSpecDriftCli } from "@deftai/directive-core/verify-source";
|
|
4
|
+
export function run(argv) {
|
|
5
|
+
const result = runSpecDriftCli(argv);
|
|
6
|
+
if (result.stdout.length > 0)
|
|
7
|
+
process.stdout.write(result.stdout);
|
|
8
|
+
if (result.stderr.length > 0)
|
|
9
|
+
process.stderr.write(result.stderr);
|
|
10
|
+
return result.exitCode;
|
|
11
|
+
}
|
|
12
|
+
if (process.argv[1] !== undefined && fileURLToPath(import.meta.url) === process.argv[1]) {
|
|
13
|
+
process.exit(run(process.argv.slice(2)));
|
|
14
|
+
}
|
|
15
|
+
//# sourceMappingURL=verify-spec-drift.js.map
|
|
@@ -95,8 +95,11 @@ const HELP_TEXT = `usage: verify-story-ready [--vbrief-path PATH] [--project-roo
|
|
|
95
95
|
[--skip-routing] [--roles ROLE[,ROLE...]]
|
|
96
96
|
|
|
97
97
|
Deterministic story-start Gate 0 (#1378). When the active dispatch provider is
|
|
98
|
-
|
|
99
|
-
|
|
98
|
+
one of ROUTING_GATED_DISPATCH_PROVIDERS (claude, cursor, grok, grok-bot,
|
|
99
|
+
openclaw), chains verify:routing (#1877 / #3703). --skip-routing records a
|
|
100
|
+
silent opt-out. Session-start --advise remains additive disclosure only and
|
|
101
|
+
does not relieve honor-at-dispatch. Three-state exit: 0 ready / 1 not ready /
|
|
102
|
+
2 config error.
|
|
100
103
|
`;
|
|
101
104
|
function emitJson(vbriefPath, exitCode, message, dispatchKind) {
|
|
102
105
|
const payload = {
|
|
@@ -129,6 +129,20 @@ export function evaluateSubagentAliveGate(args, cwd = process.cwd()) {
|
|
|
129
129
|
redispatch_ok: unhealthy && !configError,
|
|
130
130
|
required_agents: [...args.requireAgents],
|
|
131
131
|
missing_or_stale_agents: missingAgents,
|
|
132
|
+
// Flat per-agent records including wait_kind (#5278 P2).
|
|
133
|
+
records: sweep.records.map((r) => ({
|
|
134
|
+
agent_id: r.agent_id,
|
|
135
|
+
phase: r.phase,
|
|
136
|
+
wait_kind: r.wait_kind,
|
|
137
|
+
head_sha: r.head_sha,
|
|
138
|
+
pr_number: r.pr_number,
|
|
139
|
+
last_heartbeat_at: r.last_heartbeat_at_iso,
|
|
140
|
+
age_seconds: r.age_seconds,
|
|
141
|
+
is_stale: r.is_stale,
|
|
142
|
+
is_terminal: r.is_terminal,
|
|
143
|
+
failures: [...r.failures],
|
|
144
|
+
ok: recordOk(r),
|
|
145
|
+
})),
|
|
132
146
|
sweep: {
|
|
133
147
|
scratch_dirs: sweep.scratch_dirs,
|
|
134
148
|
threshold_minutes: sweep.threshold_minutes,
|
|
@@ -167,10 +181,12 @@ export function evaluateSubagentAliveGate(args, cwd = process.cwd()) {
|
|
|
167
181
|
lines.push("");
|
|
168
182
|
lines.push("REDISPATCH_OK: host-reported running + missing/STALE heartbeat authorizes takeover.");
|
|
169
183
|
lines.push("A killed worker's delivery attempt stays running until cancelled.");
|
|
170
|
-
lines.push("Takeover: task
|
|
184
|
+
lines.push("Takeover: task subagent:pre-cancel -- --agent <id> --canceller-id <id> --target-id <worktree>");
|
|
185
|
+
lines.push("then: task swarm:pre-dispatch -- --scope-id <id> --target-id <worktree> --action cancel --agent <id>");
|
|
171
186
|
lines.push("then: task swarm:pre-dispatch -- --scope-id <id> --target-id <worktree>");
|
|
172
187
|
lines.push("If verify:session-ritual --tier=gated fails, run session:start --rearm --session-id=<same> first.");
|
|
173
188
|
lines.push("Do not wait on Cursor false-alive state. Do not spawn while DENY_DUPLICATE_ACTIVE.");
|
|
189
|
+
lines.push("⊗ bare ledger cancel while pre-cancel is red (#5278).");
|
|
174
190
|
return {
|
|
175
191
|
exitCode: EXIT_STALE,
|
|
176
192
|
redispatchOk: true,
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@deftai/directive",
|
|
3
|
-
"version": "0.
|
|
3
|
+
"version": "0.121.0",
|
|
4
4
|
"description": "Directive CLI — npm install path for the Deft Directive framework.",
|
|
5
5
|
"license": "MIT",
|
|
6
6
|
"homepage": "https://deftai.github.io/directive/",
|
|
@@ -35,8 +35,8 @@
|
|
|
35
35
|
"provenance": true
|
|
36
36
|
},
|
|
37
37
|
"dependencies": {
|
|
38
|
-
"@deftai/directive-core": "^0.
|
|
39
|
-
"@deftai/directive-content": "^0.
|
|
38
|
+
"@deftai/directive-core": "^0.121.0",
|
|
39
|
+
"@deftai/directive-content": "^0.121.0"
|
|
40
40
|
},
|
|
41
41
|
"scripts": {
|
|
42
42
|
"build": "tsc -b",
|