@deksden-com/dd-flow-cli 0.8.0 → 0.9.0-beta.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/CHANGELOG.md +23 -0
- package/README.md +30 -3
- package/dist/build-info.json +4 -4
- package/dist/cli/help.js +13 -61
- package/dist/cli/run-cli.js +55 -24
- package/dist/domain/stage-catalog.js +2 -2
- package/dist/schemas/agent-profile.schema.json +17 -0
- package/dist/schemas/code-review-decision.schema.json +5 -3
- package/dist/schemas/merge-result.schema.json +15 -0
- package/dist/schemas/vnext-protocol-plan.schema.json +1 -1
- package/dist/services/cli-operation-classifier.js +1 -1
- package/dist/services/code-checks.js +125 -208
- package/dist/services/eval-snapshots.js +10 -1
- package/dist/services/harness-adapter.js +59 -0
- package/dist/services/hooks.js +87 -237
- package/dist/services/ids.js +18 -1
- package/dist/services/lifecycle-command.js +288 -0
- package/dist/services/merge-server.js +124 -0
- package/dist/services/runs.js +7 -2
- package/dist/services/sessions.js +18 -27
- package/dist/services/stage-pause.js +13 -0
- package/dist/services/usage.js +9 -17
- package/dist/services/vnext-code-review.js +70 -16
- package/dist/services/vnext-code.js +45 -18
- package/dist/services/vnext-execution-profile.js +6 -3
- package/dist/services/vnext-merge.js +330 -0
- package/dist/services/vnext-plan.js +13 -5
- package/dist/services/vnext-specify.js +5 -2
- package/dist/services/vnext-workspace-policy.js +8 -2
- package/dist/services/work-registry.js +35 -7
- package/dist/storage/database.js +66 -0
- package/package.json +2 -1
package/CHANGELOG.md
CHANGED
|
@@ -1,5 +1,28 @@
|
|
|
1
1
|
# @deksden-com/dd-flow-cli
|
|
2
2
|
|
|
3
|
+
## 0.9.0-beta.1
|
|
4
|
+
|
|
5
|
+
### Patch Changes
|
|
6
|
+
|
|
7
|
+
- 61cc92e: Finalize run usage from provider-terminal evidence even when the agent's final
|
|
8
|
+
Work lifecycle call arrives afterwards.
|
|
9
|
+
|
|
10
|
+
## 0.9.0-beta.0
|
|
11
|
+
|
|
12
|
+
### Minor Changes
|
|
13
|
+
|
|
14
|
+
- cef9b99: Add the vNext MERGE stage, project FIFO integration requests, same-session and
|
|
15
|
+
merge-server execution, integrated-tree verification receipts, and explicit
|
|
16
|
+
local delivery and cleanup policies. Upgrade the planned-check contract to
|
|
17
|
+
`code-check-profile@5` and project execution snapshots to
|
|
18
|
+
`project-execution@2`.
|
|
19
|
+
|
|
20
|
+
### Patch Changes
|
|
21
|
+
|
|
22
|
+
- 7a97bdf: Use one shell-aware lifecycle parser across every harness hook so quoted
|
|
23
|
+
absolute launchers bind correctly while diagnostic text cannot trigger a
|
|
24
|
+
trusted lifecycle event.
|
|
25
|
+
|
|
3
26
|
## 0.8.0
|
|
4
27
|
|
|
5
28
|
### 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
|
|
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
|
-
##
|
|
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
|
package/dist/build-info.json
CHANGED
|
@@ -1,11 +1,11 @@
|
|
|
1
1
|
{
|
|
2
2
|
"cli_package": "@deksden-com/dd-flow-cli",
|
|
3
|
-
"cli_version": "0.
|
|
4
|
-
"cli_commit": "
|
|
5
|
-
"built_at": "2026-
|
|
3
|
+
"cli_version": "0.9.0-beta.1",
|
|
4
|
+
"cli_commit": "5e493443b9f97ad3c83e0edccc29dfbee584b92e",
|
|
5
|
+
"built_at": "2026-09-01T21:02:46.536Z",
|
|
6
6
|
"built_with_canon": {
|
|
7
7
|
"version": "4.0.0",
|
|
8
|
-
"commit": "
|
|
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
|
|
32
|
-
merge
|
|
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
|
|
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 -
|
|
366
|
+
`dd-flow merge - deterministic vNext integration queue and executor
|
|
367
367
|
|
|
368
368
|
Usage:
|
|
369
|
-
dd-flow merge status --project-root <root>
|
|
370
|
-
dd-flow merge
|
|
371
|
-
dd-flow merge
|
|
372
|
-
dd-flow merge
|
|
373
|
-
dd-flow merge
|
|
374
|
-
dd-flow merge
|
|
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
|
-
|
|
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",
|
package/dist/cli/run-cli.js
CHANGED
|
@@ -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
|
-
|
|
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
|
-
|
|
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" &&
|
|
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@
|
|
9
|
-
{ id: "merge", directory: "07-merge", result: "
|
|
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@
|
|
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@
|
|
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")
|