@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.
@@ -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 result = evaluateCompletedTracked(projectRoot, {
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
- if (result.message.length > 0) {
121
- if (result.stream === "stdout") {
122
- process.stdout.write(`${result.message}\n`);
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 (result.stream === "stderr") {
125
- process.stderr.write(`${result.message}\n`);
161
+ else if (land.stream === "stderr") {
162
+ process.stderr.write(`${land.message}\n`);
126
163
  }
127
164
  }
128
- return result.code;
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: args.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,3 @@
1
+ #!/usr/bin/env node
2
+ export declare function run(argv: string[]): number;
3
+ //# sourceMappingURL=verify-spec-drift.d.ts.map
@@ -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
- cursor or grok, chains verify:routing (#1877 single-dispatch enforcement).
99
- Three-state exit: 0 ready / 1 not ready / 2 config error.
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 swarm:pre-dispatch -- --scope-id <id> --target-id <worktree> --action cancel");
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.119.13",
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.119.13",
39
- "@deftai/directive-content": "^0.119.13"
38
+ "@deftai/directive-core": "^0.121.0",
39
+ "@deftai/directive-content": "^0.121.0"
40
40
  },
41
41
  "scripts": {
42
42
  "build": "tsc -b",