@deksden-com/dd-flow-cli 0.8.0 → 0.9.0-beta.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/CHANGELOG.md CHANGED
@@ -1,5 +1,21 @@
1
1
  # @deksden-com/dd-flow-cli
2
2
 
3
+ ## 0.9.0-beta.0
4
+
5
+ ### Minor Changes
6
+
7
+ - cef9b99: Add the vNext MERGE stage, project FIFO integration requests, same-session and
8
+ merge-server execution, integrated-tree verification receipts, and explicit
9
+ local delivery and cleanup policies. Upgrade the planned-check contract to
10
+ `code-check-profile@5` and project execution snapshots to
11
+ `project-execution@2`.
12
+
13
+ ### Patch Changes
14
+
15
+ - 7a97bdf: Use one shell-aware lifecycle parser across every harness hook so quoted
16
+ absolute launchers bind correctly while diagnostic text cannot trigger a
17
+ trusted lifecycle event.
18
+
3
19
  ## 0.8.0
4
20
 
5
21
  ### Patch Changes
package/README.md CHANGED
@@ -106,8 +106,7 @@ dd-flow --help
106
106
  dd-flow lane --help
107
107
  dd-flow lane workspace --help
108
108
  dd-flow lane lock --help
109
- dd-flow merge-queue --help
110
- dd-flow merge-queue wait-next --help
109
+ dd-flow merge --help
111
110
  dd-flow cleanup --help
112
111
  dd-flow codex home --help
113
112
  dd-flow worktree --help
@@ -189,7 +188,29 @@ When the task stops using the shared checkout:
189
188
  dd-flow lane lock release --project-root "$PROJECT_ROOT" --lane direct-main --worker-id "$PROTOCOL_ID" --path "$PWD" --lease-token "$TOKEN" --reason done --json
190
189
  ```
191
190
 
192
- ## Merge Queue Recipe
191
+ ## vNext MERGE recipe
192
+
193
+ CODE/CODE-REVIEW closure creates the `MRG-*` request and returns the exact
194
+ standalone stage-start command. Run it unchanged. The accepted packet then
195
+ provides the exact `merge apply` and `stage finish` commands; do not reconstruct
196
+ Git operations or lifecycle arguments manually.
197
+
198
+ For unattended execution, store a validated agent profile under
199
+ `DD_FLOW_HOME/agent-profiles/<id>.json` and run:
200
+
201
+ ```bash
202
+ dd-flow merge serve --agent-profile <id> --max-parallel-projects 4 --progress-jsonl
203
+ dd-flow merge server status --json
204
+ ```
205
+
206
+ The server preserves FIFO inside one project and may serve different projects
207
+ concurrently. Same-session and server routes use the same MRG, Work, checks and
208
+ stage report.
209
+
210
+ ## Classic merge queue recipe
211
+
212
+ The commands below belong to the classic protocol runtime. The vNext flow does
213
+ not call them or fall back to them.
193
214
 
194
215
  Prompts enqueue protocols only after explicit readiness signals exist:
195
216
 
@@ -368,6 +389,12 @@ The installed hook writes invocation markers, forwarded stderr, and the final
368
389
  exit code to `${DD_FLOW_HOME:-$HOME/.dd-flow}/logs/codex-hook.log`. An absent
369
390
  start marker means Codex did not invoke the hook; a start marker without a
370
391
  successful finish marker identifies a hook launch or handler failure.
392
+
393
+ All harness event handlers use the same lifecycle shell parser. It recognizes
394
+ `dd-flow` as the actual command word, including a quoted absolute executable,
395
+ and does not treat occurrences in search patterns, JSON, comments or heredoc
396
+ bodies as lifecycle calls. Real lifecycle calls joined to other shell commands
397
+ remain invalid and receive a standalone retry.
371
398
  `--project-root` belongs to installer/status commands, not the stored handler.
372
399
 
373
400
  ## Verification
@@ -1,11 +1,11 @@
1
1
  {
2
2
  "cli_package": "@deksden-com/dd-flow-cli",
3
- "cli_version": "0.8.0",
4
- "cli_commit": "f0a5e35190c960c16caa0913837d4426b3c35db7",
5
- "built_at": "2026-08-31T10:23:34.510Z",
3
+ "cli_version": "0.9.0-beta.0",
4
+ "cli_commit": "ade3ebb2888bf804bec3879bbf7ed4600ff651fc",
5
+ "built_at": "2026-09-01T20:01:20.985Z",
6
6
  "built_with_canon": {
7
7
  "version": "4.0.0",
8
- "commit": "39728d7091691ad32e94d7b79b3656821c63ccc2",
8
+ "commit": "bd6494ad11bca9544d9b483bd7fe37049e6d00fe",
9
9
  "flow_contract": "dd-flow-canonical-2026-08",
10
10
  "repo_root": "/Users/deksden/Documents/_Projects/dd-memorybank",
11
11
  "memorybank_root": "/Users/deksden/Documents/_Projects/dd-memorybank/.memory-bank",
package/dist/cli/help.js CHANGED
@@ -28,9 +28,8 @@ Core commands:
28
28
  plan status/item/reviews Inspect plans, update SQLite progress, or dispatch vNext grouped reviews.
29
29
  prompt render Render one bounded, RUN-local worker launch prompt.
30
30
  lane workspace/lock/status Manage shared workspace lanes and leases.
31
- merge status/one-shot/bundle Inspect, claim, or complete merge work from the current session.
32
- merge-worker status/start/stop Manage long-lived project merge workers.
33
- merge-queue status/claim/next Claim, wait for, complete, or fail merge jobs.
31
+ merge request/apply Inspect, route and execute vNext MERGE requests.
32
+ merge serve/status/stop Run or inspect the deterministic vNext merge server.
34
33
  session register/status/stop Register flow sessions and stop workers.
35
34
  dashboard data/render/open/refresh Render, refresh, open, and inspect project/global dashboards.
36
35
  schema validate Validate canonical dd-flow JSON data contracts.
@@ -60,8 +59,9 @@ Examples:
60
59
  dd-flow protocol transition PRT-001-demo --to implementation --payload-file transition.json --json
61
60
  dd-flow lane workspace set --project-root "$PWD" --lane merge --path "$PWD" --branch main --json
62
61
  dd-flow lane lock acquire --project-root "$PWD" --lane merge --worker-id worker-1 --path "$PWD" --ttl 300 --reason "merge worker" --json
63
- dd-flow merge status --project-root "$PWD" --json
64
- dd-flow merge-worker start --project-root "$PWD" --worker-id merge-worker --path "$PWD" --json
62
+ dd-flow merge request status MRG-001 --project-root "$PWD" --json
63
+ dd-flow merge apply MRG-001 --work WRK-001 --project-root "$PWD" --json --progress-jsonl
64
+ dd-flow merge serve --agent-profile codex-luna-xhigh --progress-jsonl
65
65
 
66
66
  Default output is human-readable. Use --json for hooks, prompts, scripts and other agent automation; JSON mode never mixes human progress text into stdout.
67
67
 
@@ -363,53 +363,17 @@ The renderer accepts only registered profiles: code_implementation, documentatio
363
363
  ],
364
364
  [
365
365
  "merge",
366
- `dd-flow merge - current-session merge entrypoint helpers
366
+ `dd-flow merge - deterministic vNext integration queue and executor
367
367
 
368
368
  Usage:
369
- dd-flow merge status --project-root <root> [--path <workspace>] --json
370
- dd-flow merge one-shot --project-root <root> --worker-id <id> [--path <workspace>] --json
371
- dd-flow merge bundle status --project-root <root> --path <workspace> --json
372
- dd-flow merge bundle claim --project-root <root> --worker-id <id> --path <workspace> --json
373
- dd-flow merge bundle complete --project-root <root> --worker-id <id> --path <workspace> --summary <text> --json
374
- dd-flow merge bundle fail --project-root <root> --worker-id <id> --path <workspace> --reason <text> --requeue true|false --json
369
+ dd-flow merge request status <MRG-ID> --project-root <root> --json
370
+ dd-flow merge request route <MRG-ID> --mode same_session|server --reason <text> --project-root <root> --json
371
+ dd-flow merge apply <MRG-ID> --work <WRK-ID> --project-root <root> --json --progress-jsonl
372
+ dd-flow merge serve --agent-profile <id> [--once] [--poll-seconds <n>] [--max-parallel-projects <n>] --progress-jsonl
373
+ dd-flow merge server status --json
374
+ dd-flow merge server stop <MSV-ID> --json
375
375
 
376
- merge status reports active merge worker, claimed protocols, active merge lane lock, queue state and branch_context when --path is provided. merge one-shot keeps legacy single-protocol behavior and also returns branch_context. merge bundle commands operate on all ready protocols in the current branch/worktree: status is read-only, claim requires the merge lane lock owner, complete closes all claimed protocols after branch integration, and fail requeues or fails the claimed bundle.`
377
- ],
378
- [
379
- "merge-worker",
380
- `dd-flow merge-worker - manage long-lived project merge workers
381
-
382
- Usage:
383
- dd-flow merge-worker status --project-root <root> --json
384
- dd-flow merge-worker start --project-root <root> --worker-id <id> [--path <workspace>] [--branch <branch>] --json
385
- dd-flow merge-worker stop --project-root <root> [--worker-id <id>] --reason <text> --json
386
-
387
- start is idempotent: if a worker, claimed job, or active merge lock already exists it returns status instead of creating a duplicate worker. stop is no-op/status when no worker exists, refuses ambiguous multiple workers unless --worker-id is provided, stops idle workers immediately, and marks busy workers as stop-after-current while preserving the claimed job and lock.`
388
- ],
389
- [
390
- "merge-queue",
391
- `dd-flow merge-queue - inspect and process ready-for-merge protocols
392
-
393
- Usage:
394
- dd-flow merge-queue status --project-root <root> --json
395
- dd-flow merge-queue next --project-root <root> --worker-id <id> [--path <workspace>] --json
396
- dd-flow merge-queue claim <protocol-id> [--project-root <root>] --worker-id <id> [--path <workspace>] --json
397
- dd-flow merge-queue wait-next --project-root <root> --worker-id <id> [--path <workspace>] --timeout <seconds> --poll-interval <seconds> [--acquire-lock true] --json
398
- dd-flow merge-queue complete <protocol-id> [--project-root <root>] --worker-id <id> [--path <workspace>] --summary <text> --json
399
- dd-flow merge-queue note <protocol-id> [--project-root <root>] --worker-id <id> --summary <text> --json
400
- dd-flow merge-queue fail <protocol-id> [--project-root <root>] --worker-id <id> [--path <workspace>] --reason <text> --requeue true|false --json
401
- dd-flow merge-queue cancel <protocol-id> [--project-root <root>] --reason <text> [--worker-id <id>] [--path <workspace>] [--force] --json
402
-
403
- The worker must own the merge lane from the registered merge workspace before claiming, completing, or failing queued protocols. next remains FIFO; claim <protocol-id> atomically claims only that ready/requeued job. Outputs use queue_item/protocol/claim as the primary contract and retain job as a compatibility alias. note updates the final completion summary after post-complete cleanup and does not require a lane lock. --path defaults to the current working directory. Use --worker-id as the owner identity.
404
-
405
- For branch-level integration prefer:
406
- dd-flow merge bundle status --project-root "$PWD" --path "$PWD" --json
407
- dd-flow merge bundle claim --project-root "$PWD" --worker-id merge-worker --path "$PWD" --json
408
-
409
- Examples:
410
- dd-flow merge-queue wait-next --project-root "$PWD" --worker-id merge-worker --path "$PWD" --timeout 1200 --poll-interval 10 --acquire-lock true --json
411
- dd-flow merge-queue complete PRT-123 --project-root "$PWD" --worker-id merge-worker --path "$PWD" --summary "merged locally; cleanup follows" --json
412
- dd-flow merge-queue note PRT-123 --project-root "$PWD" --worker-id merge-worker --summary "merged, pushed, checked, and cleanup completed" --json`
376
+ CODE or CODE-REVIEW creates one MRG request and child MERGE Work. stage start waits in the shared project FIFO, reports progress, locks the execution target baseline and returns the exact apply/finish commands. merge apply is the only supported initial Git mutation. merge serve is a deterministic dispatcher: it launches a fresh Session through the configured harness adapter but does not perform agent work itself. It preserves FIFO inside each project and may serve different projects concurrently up to the explicit bound.`
413
377
  ],
414
378
  [
415
379
  "cleanup",
@@ -420,18 +384,6 @@ Usage:
420
384
  dd-flow cleanup apply --project-root <root> --plan-file <file> --reason <text> [--force] --json
421
385
 
422
386
  cleanup scan is read-only and returns deterministic findings plus plan.actions. cleanup apply validates project identity, action kinds, reason, and destructive force requirements before mutating runtime state, then reports requested/applied/changed/skipped action results. Worktree record cleanup marks a checkout removed only when its path is already absent; existing checkouts are kept instead of being silently deleted. Use this for stale queue jobs, missing protocol runtime snapshots, stale sessions, expired locks, and terminal-protocol worktree records.`
423
- ],
424
- [
425
- "merge-queue wait-next",
426
- `dd-flow merge-queue wait-next - wait for the next ready merge protocol
427
-
428
- Usage:
429
- dd-flow merge-queue wait-next --project-root <root> --worker-id <id> [--path <workspace>] --timeout <seconds> --poll-interval <seconds> [--acquire-lock true] --json
430
-
431
- Use this from a shell or supervisor to wait outside model tokens. Live merge workers should prefer long waits, for example --timeout 1200 --poll-interval 10, so the agent sleeps inside the CLI process instead of spending model tokens on frequent polling. If --acquire-lock true is used, wait-next first enters the generic FIFO lane waiter path (lane lock wait-acquire --lane merge), then heartbeats the merge lock while waiting for a ready protocol. A lock acquired by this command is released on timeout. If a protocol is ready, it is claimed atomically. If no protocol appears before the timeout, the command returns outcome: wait_timeout with queue_item: null and legacy job: null.
432
-
433
- Examples:
434
- dd-flow merge-queue wait-next --project-root "$PWD" --worker-id supervisor --path "$PWD" --timeout 1200 --poll-interval 10 --acquire-lock true --json`
435
387
  ],
436
388
  [
437
389
  "session",
@@ -17,6 +17,7 @@ import { getProjectConfigStatus, setProjectConfigValue } from "../services/confi
17
17
  import { getCanonStatus, registerCanonRoot, resolveCanonRoot } from "../services/canon.js";
18
18
  import { getRuntimeStatus } from "../services/status.js";
19
19
  import { getCliVersionReport } from "../services/build-info.js";
20
+ import { parseCommandArgs } from "../services/lifecycle-command.js";
20
21
  import { autoRefreshDashboards, dashboardData, getCmuxStatus, openDashboard, openGlobalDashboard, openProtocolDashboard, refreshAllDashboards, refreshDashboard, refreshGlobalDashboard, renderDashboard, renderGlobalDashboard } from "../services/dashboard.js";
21
22
  import { dashboardTargetSummary, preferredDashboardCommand, relatedDashboardCommands, resolveDashboardTarget } from "../services/dashboard-targets.js";
22
23
  import { getFlowSessionStatus, registerFlowSession, stopFlowSession, stopMergeWorker, syncFlowSessionUsage } from "../services/sessions.js";
@@ -38,6 +39,8 @@ import { finishVnextPlan, isVnextPlanRun, startVnextPlan } from "../services/vne
38
39
  import { dispatchVnextPlanReview, finishVnextPlanReview, isVnextPlanReviewRun, recordVnextPlanReviewCapacity, startVnextPlanReview } from "../services/vnext-plan-review.js";
39
40
  import { addVnextCodeRepair, finishVnextCode, startVnextCode } from "../services/vnext-code.js";
40
41
  import { addVnextCodeReviewRepair, finishVnextCodeReview, isVnextCodeReviewRun, startVnextCodeReview } from "../services/vnext-code-review.js";
42
+ import { applyVnextMerge, finishVnextMerge, getVnextMergeRequest, isVnextMergeRun, routeVnextMergeRequest, startVnextMerge } from "../services/vnext-merge.js";
43
+ import { mergeServerStatus, serveMergeRequests, stopMergeServer } from "../services/merge-server.js";
41
44
  import { getVnextFanoutStatus } from "../services/vnext-fanout.js";
42
45
  import { pauseStageForUser, resumeStageAfterUser } from "../services/stage-pause.js";
43
46
  import { blockStageForRuntime, unblockStageAfterRuntimeRepair } from "../services/stage-blocker.js";
@@ -217,7 +220,7 @@ function progressForCommand(args) {
217
220
  ];
218
221
  }
219
222
  if ((family === "cleanup" && command === "apply") ||
220
- (family === "merge" && command === "one-shot") ||
223
+ family === "merge" ||
221
224
  family === "merge-worker" ||
222
225
  (family === "protocol" && command === "cancel") ||
223
226
  (family === "project" && command === "archive") ||
@@ -666,6 +669,7 @@ async function dispatch(args, context, io, scopeProjectRoot = null, classificati
666
669
  projectRoot: requiredScopeProjectRoot(scopeProjectRoot, "work repair add"),
667
670
  runId: requiredOption(parsed, "run"),
668
671
  findingIds: findings,
672
+ checkRefsByFinding: Object.fromEntries(findings.map((finding) => [finding, parsed.options.get("check-ref") ?? []])),
669
673
  objective: await readStdin(io.stdin)
670
674
  });
671
675
  if (!hasOption(parsed, "from-check"))
@@ -781,6 +785,10 @@ async function dispatch(args, context, io, scopeProjectRoot = null, classificati
781
785
  hookEventId ??= claimVnextStageStart(requiredPosition(parsed, 0, "run-id"));
782
786
  return attachExternalStageContext(startVnextCodeReview(context, { projectRoot, runId: requiredPosition(parsed, 0, "run-id"), hookEventId, ...(contextSha256 ? { contextSha256 } : {}), ...(externalContext ? { externalContext } : {}) }), externalContext);
783
787
  }
788
+ if (!bootstrap && stage === "merge" && isVnextMergeRun(context, { projectRoot, runId: requiredPosition(parsed, 0, "run-id") })) {
789
+ hookEventId ??= claimVnextStageStart(requiredPosition(parsed, 0, "run-id"));
790
+ return attachExternalStageContext(await startVnextMerge(context, { projectRoot, runId: requiredPosition(parsed, 0, "run-id"), hookEventId, progress: commandProgress(io, output), ...(contextSha256 ? { contextSha256 } : {}), ...(externalContext ? { externalContext } : {}) }), externalContext);
791
+ }
784
792
  if (intakeStdin) {
785
793
  throw new AppError("usage", "--intake-stdin is only available for vNext SPECIFY", 2);
786
794
  }
@@ -853,6 +861,11 @@ async function dispatch(args, context, io, scopeProjectRoot = null, classificati
853
861
  throw new AppError("usage", "vNext CODE-REVIEW uses --decision-file", 2);
854
862
  return await finishVnextCodeReview(context, { projectRoot, runId, ...(optionalOption(parsed, "decision-file") ? { decisionFile: optionalOption(parsed, "decision-file") } : {}), progress: commandProgress(io, output) });
855
863
  }
864
+ if (stage === "merge" && isVnextMergeRun(context, { projectRoot, runId })) {
865
+ if (resultStdin || semanticFile || resultFile || hasOption(parsed, "outcome"))
866
+ throw new AppError("usage", "vNext MERGE uses the Work result path returned by stage start", 2);
867
+ return await finishVnextMerge(context, { projectRoot, runId, requestId: requiredOption(parsed, "request"), workId: requiredOption(parsed, "work"), progress: commandProgress(io, output) });
868
+ }
856
869
  if (hasOption(parsed, "outcome") || hasOption(parsed, "result-file") || resultStdin) {
857
870
  throw new AppError("usage", "--outcome, --result-file and --result-stdin are only available for vNext SPECIFY", 2);
858
871
  }
@@ -920,7 +933,7 @@ async function dispatch(args, context, io, scopeProjectRoot = null, classificati
920
933
  return dispatchMergeQueue(context, command, parsed, requiredScopeProjectRootForOptional(scopeProjectRoot, command, parsed));
921
934
  }
922
935
  if (family === "merge") {
923
- return dispatchMerge(context, command, parsed);
936
+ return await dispatchMerge(context, command, parsed, commandProgress(io, output));
924
937
  }
925
938
  if (family === "merge-worker") {
926
939
  return dispatchMergeWorker(context, command, parsed);
@@ -1156,7 +1169,35 @@ function dispatchMergeQueue(context, command, parsed, scopeProjectRoot) {
1156
1169
  }
1157
1170
  throw new AppError("usage", `Unknown merge-queue command: ${command ?? "<empty>"}`, 2);
1158
1171
  }
1159
- function dispatchMerge(context, command, parsed) {
1172
+ async function dispatchMerge(context, command, parsed, progress) {
1173
+ if (command === "serve") {
1174
+ const pollSeconds = optionalPositiveNumber(parsed, "poll-seconds");
1175
+ const maxParallelProjects = optionalPositiveNumber(parsed, "max-parallel-projects", true);
1176
+ return await serveMergeRequests(context, { profileId: requiredOption(parsed, "agent-profile"), once: hasOption(parsed, "once"), ...(pollSeconds ? { pollSeconds } : {}), ...(maxParallelProjects ? { maxParallelProjects } : {}), ...(progress ? { progress } : {}) });
1177
+ }
1178
+ if (command === "server") {
1179
+ const action = requiredPosition(parsed, 0, "server action");
1180
+ if (action === "status")
1181
+ return mergeServerStatus(context);
1182
+ if (action === "stop")
1183
+ return stopMergeServer(context, { serverId: requiredPosition(parsed, 1, "server-id") });
1184
+ throw new AppError("usage", `Unknown merge server action: ${action}`, 2);
1185
+ }
1186
+ if (command === "apply")
1187
+ return applyVnextMerge(context, { projectRoot: requiredOption(parsed, "project-root"), requestId: requiredPosition(parsed, 0, "merge-request-id"), workId: requiredOption(parsed, "work"), ...(progress ? { progress } : {}) });
1188
+ if (command === "request") {
1189
+ const action = requiredPosition(parsed, 0, "request action");
1190
+ const requestId = requiredPosition(parsed, 1, "merge-request-id");
1191
+ if (action === "status")
1192
+ return getVnextMergeRequest(context, { projectRoot: requiredOption(parsed, "project-root"), requestId });
1193
+ if (action === "route") {
1194
+ const mode = requiredOption(parsed, "mode");
1195
+ if (mode !== "same_session" && mode !== "server")
1196
+ throw new AppError("usage", "--mode must be same_session or server", 2);
1197
+ return routeVnextMergeRequest(context, { projectRoot: requiredOption(parsed, "project-root"), requestId, mode, reason: requiredOption(parsed, "reason") });
1198
+ }
1199
+ throw new AppError("usage", `Unknown merge request action: ${action}`, 2);
1200
+ }
1160
1201
  if (command === "status") {
1161
1202
  return getMergeWorkerStatus(context, { projectRoot: requiredOption(parsed, "project-root"), workspacePath: optionalOption(parsed, "path") });
1162
1203
  }
@@ -1987,26 +2028,7 @@ function parseFlowFlagOverrides(values) {
1987
2028
  return result;
1988
2029
  }
1989
2030
  function parseArgs(args) {
1990
- const positional = [];
1991
- const options = new Map();
1992
- for (let index = 0; index < args.length; index += 1) {
1993
- const value = args[index];
1994
- if (value?.startsWith("--")) {
1995
- const key = value.slice(2);
1996
- const next = args[index + 1];
1997
- if (!next || next.startsWith("--")) {
1998
- options.set(key, [...(options.get(key) ?? []), flagValue]);
1999
- }
2000
- else {
2001
- options.set(key, [...(options.get(key) ?? []), next]);
2002
- index += 1;
2003
- }
2004
- }
2005
- else if (value) {
2006
- positional.push(value);
2007
- }
2008
- }
2009
- return { positional, options };
2031
+ return parseCommandArgs(args);
2010
2032
  }
2011
2033
  function requiredPosition(parsed, index, label) {
2012
2034
  const value = parsed.positional[index];
@@ -2080,6 +2102,15 @@ function optionalNumber(parsed, key) {
2080
2102
  }
2081
2103
  return parsedNumber;
2082
2104
  }
2105
+ function optionalPositiveNumber(parsed, key, integer = false) {
2106
+ const value = optionalNumber(parsed, key);
2107
+ if (typeof value === "undefined")
2108
+ return undefined;
2109
+ if (value <= 0 || (integer && !Number.isInteger(value))) {
2110
+ throw new AppError("validation", `--${key} must be a positive${integer ? " integer" : ""} number`, 2);
2111
+ }
2112
+ return value;
2113
+ }
2083
2114
  function requiredWorkerId(parsed) {
2084
2115
  return requiredOption(parsed, "worker-id");
2085
2116
  }
@@ -2174,7 +2205,7 @@ function projectRootForMutation(context, args, result, resolvedScope) {
2174
2205
  if (family === "merge-queue") {
2175
2206
  return projectRootForMergeQueueMutation(context, command, parsed);
2176
2207
  }
2177
- if (family === "merge" && command === "one-shot") {
2208
+ if (family === "merge" && ["one-shot", "apply", "request"].includes(command ?? "")) {
2178
2209
  return requiredOption(parsed, "project-root");
2179
2210
  }
2180
2211
  if (family === "merge-worker" && ["start", "stop"].includes(command ?? "")) {
@@ -5,8 +5,8 @@ export const vnextStages = [
5
5
  { id: "plan", directory: "03-plan", result: "stage-report.json", schema: "dd-flow/stage-report@2", next: ["plan-review", "code"] },
6
6
  { id: "plan-review", directory: "04-plan-review", result: "decision.json", schema: "dd-flow/plan-review-decision@3", next: ["code"] },
7
7
  { id: "code", directory: "05-code", result: "code-verification.json", schema: "dd-flow/code-verification@2", next: ["code-review", "merge"] },
8
- { id: "code-review", directory: "06-code-review", result: "decision.json", schema: "dd-flow/code-review-decision@2", next: ["merge"] },
9
- { id: "merge", directory: "07-merge", result: "merge-result.json", schema: "dd-flow/merge-result@1", next: [] }
8
+ { id: "code-review", directory: "06-code-review", result: "decision.json", schema: "dd-flow/code-review-decision@3", next: ["merge"] },
9
+ { id: "merge", directory: "07-merge", result: "stage-report.json", schema: "dd-flow/stage-report@2", next: [] }
10
10
  ];
11
11
  export function vnextStage(id) {
12
12
  const stage = vnextStages.find((candidate) => candidate.id === id);
@@ -0,0 +1,17 @@
1
+ {
2
+ "$schema": "https://json-schema.org/draft/2020-12/schema",
3
+ "$id": "dd-flow/agent-profile@1",
4
+ "type": "object",
5
+ "additionalProperties": false,
6
+ "required": ["schema_id", "id", "harness", "provider", "model", "reasoning", "mode", "permission"],
7
+ "properties": {
8
+ "schema_id": {"const": "dd-flow/agent-profile@1"},
9
+ "id": {"type": "string", "pattern": "^[A-Za-z0-9][A-Za-z0-9._-]*$"},
10
+ "harness": {"enum": ["codex", "zcode", "agy", "opencode", "grok"]},
11
+ "provider": {"type": "string", "minLength": 1},
12
+ "model": {"type": "string", "minLength": 1},
13
+ "reasoning": {"type": "string", "minLength": 1},
14
+ "mode": {"type": "string", "minLength": 1},
15
+ "permission": {"enum": ["allow", "deny"]}
16
+ }
17
+ }
@@ -1,11 +1,11 @@
1
1
  {
2
2
  "$schema": "https://json-schema.org/draft/2020-12/schema",
3
- "$id": "dd-flow/code-review-decision@2",
3
+ "$id": "dd-flow/code-review-decision@3",
4
4
  "type": "object",
5
5
  "additionalProperties": false,
6
6
  "required": ["schema_id", "summary", "findings"],
7
7
  "properties": {
8
- "schema_id": {"const": "dd-flow/code-review-decision@2"},
8
+ "schema_id": {"const": "dd-flow/code-review-decision@3"},
9
9
  "summary": {"type": "string", "minLength": 1},
10
10
  "findings": {
11
11
  "type": "array",
@@ -17,9 +17,11 @@
17
17
  "finding_ref": {"type": "string", "pattern": "^WRK-[^/]+/FIND-[0-9]{3}$"},
18
18
  "disposition": {"enum": ["fix", "defer", "reject", "duplicate"]},
19
19
  "reason": {"type": "string", "minLength": 1},
20
+ "check_refs": {"type": "array", "minItems": 1, "uniqueItems": true, "items": {"type": "string", "pattern": "^CHK-[A-Za-z0-9-]+$"}},
20
21
  "def_id": {"type": "string", "minLength": 1},
21
22
  "duplicate_of": {"type": "string", "minLength": 1}
22
- }
23
+ },
24
+ "allOf": [{"if": {"properties": {"disposition": {"const": "fix"}}}, "then": {"required": ["check_refs"]}}]
23
25
  }
24
26
  }
25
27
  }
@@ -0,0 +1,15 @@
1
+ {
2
+ "$schema": "https://json-schema.org/draft/2020-12/schema",
3
+ "$id": "dd-flow/merge-result@1",
4
+ "type": "object",
5
+ "additionalProperties": false,
6
+ "required": ["schema_id", "outcome", "summary", "conflict_resolution", "verification_summary", "residual_risks"],
7
+ "properties": {
8
+ "schema_id": {"const": "dd-flow/merge-result@1"},
9
+ "outcome": {"enum": ["completed", "blocked"]},
10
+ "summary": {"type": "string", "minLength": 1},
11
+ "conflict_resolution": {"type": "string"},
12
+ "verification_summary": {"type": "string", "minLength": 1},
13
+ "residual_risks": {"type": "array", "items": {"type": "string", "minLength": 1}}
14
+ }
15
+ }
@@ -29,7 +29,7 @@
29
29
  "documentUpdate": {"type": "object", "additionalProperties": false, "required": ["path", "action", "owner", "reason"], "properties": {"path": {"type": "string", "minLength": 1}, "action": {"enum": ["create", "update"]}, "owner": {"type": "string", "minLength": 1}, "reason": {"type": "string", "minLength": 1}}},
30
30
  "spine": {"type": "object", "additionalProperties": false, "required": ["user_outcome", "component_responsibility", "must_preserve", "non_goals", "acceptance_contribution"], "properties": {"user_outcome": {"type": "string", "minLength": 1}, "component_responsibility": {"type": "string", "minLength": 1}, "must_preserve": {"$ref": "#/$defs/strings"}, "non_goals": {"type": "array", "items": {"type": "string", "minLength": 1}}, "acceptance_contribution": {"type": "string", "minLength": 1}}},
31
31
  "execution": {"type": "object", "additionalProperties": false, "required": ["required_read", "discovery_boundary", "planned_write_areas", "stop_conditions"], "properties": {"required_read": {"$ref": "#/$defs/strings", "description": "Mandatory starting sources. This is not a read allowlist."}, "discovery_boundary": {"$ref": "#/$defs/strings", "description": "Likely discovery areas. A worker may inspect other project-local sources when necessary."}, "planned_write_areas": {"type": "array", "items": {"type": "string", "minLength": 1}, "description": "Optional file or directory hints used only to coordinate concurrent Works. They never grant or deny write permission."}, "stop_conditions": {"$ref": "#/$defs/strings", "description": "Semantic contradictions or external blockers that require the worker to stop."}}},
32
- "check": {"type": "object", "additionalProperties": false, "required": ["id", "command", "purpose", "run_at", "availability"], "properties": {"id": {"type": "string", "pattern": "^CHK-[A-Za-z0-9-]+$"}, "command": {"type": "string", "minLength": 1}, "purpose": {"type": "string", "minLength": 1}, "run_at": {"enum": ["work", "code", "readiness", "merge", "release", "external"]}, "availability": {"enum": ["available", "planned"]}, "provided_by": {"type": "string", "pattern": "^P[0-9]+$"}, "definition": {"type": "string", "minLength": 1}, "required_artifacts": {"type": "array", "uniqueItems": true, "items": {"type": "string", "minLength": 1}}}, "allOf": [{"if": {"properties": {"availability": {"const": "planned"}}, "required": ["availability"]}, "then": {"required": ["provided_by", "definition"], "properties": {"command": {"pattern": "^@check/"}}}}]},
32
+ "check": {"type": "object", "additionalProperties": false, "required": ["id", "command", "purpose", "run_at", "availability"], "properties": {"id": {"type": "string", "pattern": "^CHK-[A-Za-z0-9-]+$"}, "command": {"type": "string", "minLength": 1}, "purpose": {"type": "string", "minLength": 1}, "run_at": {"enum": ["work", "code", "readiness", "merge", "release", "external"]}, "availability": {"enum": ["available", "planned"]}, "provided_by": {"type": "string", "pattern": "^P[0-9]+$"}, "definition": {"type": "string", "minLength": 1}, "required_artifacts": {"type": "array", "uniqueItems": true, "items": {"type": "string", "minLength": 1}}}, "allOf": [{"if": {"properties": {"availability": {"const": "planned"}}, "required": ["availability"]}, "then": {"required": ["provided_by", "definition"], "properties": {"command": {"pattern": "^@check/"}}}}, {"if": {"properties": {"command": {"pattern": "^@check/"}}}, "then": {"required": ["definition"]}}]},
33
33
  "verification": {"type": "object", "additionalProperties": false, "required": ["check_refs"], "properties": {"check_refs": {"type": "array", "minItems": 1, "uniqueItems": true, "items": {"type": "string", "pattern": "^CHK-[A-Za-z0-9-]+$"}}}},
34
34
  "item": {"type": "object", "additionalProperties": false, "required": ["id", "title", "summary", "details", "depends_on", "requirement_refs", "semantic_spine", "execution_context", "verification"], "properties": {"id": {"type": "string", "pattern": "^P[0-9]+$"}, "title": {"type": "string", "minLength": 1}, "summary": {"type": "string", "minLength": 1}, "details": {"type": "string", "minLength": 1}, "depends_on": {"type": "array", "uniqueItems": true, "items": {"type": "string", "pattern": "^P[0-9]+$"}}, "requirement_refs": {"$ref": "#/$defs/strings"}, "semantic_spine": {"$ref": "#/$defs/spine"}, "execution_context": {"$ref": "#/$defs/execution"}, "verification": {"$ref": "#/$defs/verification"}}},
35
35
  "acceptance": {"type": "object", "additionalProperties": false, "required": ["criterion_id", "plan_item_ids", "changed_surfaces", "path", "environment", "fixtures", "cleanup", "check_refs", "expected_evidence", "proof_limits", "gate"], "properties": {"criterion_id": {"type": "string", "pattern": "^AC-[0-9]+$"}, "plan_item_ids": {"type": "array", "minItems": 1, "items": {"type": "string", "pattern": "^P[0-9]+$"}}, "changed_surfaces": {"$ref": "#/$defs/strings"}, "path": {"type": "string", "minLength": 1}, "environment": {"type": "string", "minLength": 1}, "fixtures": {"type": "array", "items": {"type": "string", "minLength": 1}}, "cleanup": {"type": "string", "minLength": 1}, "check_refs": {"type": "array", "minItems": 1, "uniqueItems": true, "items": {"type": "string", "pattern": "^CHK-[A-Za-z0-9-]+$"}}, "expected_evidence": {"$ref": "#/$defs/strings"}, "proof_limits": {"$ref": "#/$defs/strings"}, "gate": {"enum": ["work", "code", "readiness", "merge", "release", "external"]}}}
@@ -133,7 +133,7 @@ function isReadOnlyDiagnostic(family, command, action) {
133
133
  (command === "workspace" && action === "check"));
134
134
  }
135
135
  if (family === "merge")
136
- return command === "status";
136
+ return command === "status" || (command === "request" && action === "status") || (command === "server" && action === "status");
137
137
  if (family === "merge-worker")
138
138
  return command === "status";
139
139
  if (family === "merge-queue")