@deksden-com/dd-flow-cli 0.9.0-beta.114 → 0.9.0-beta.115
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 +6 -0
- package/dist/build-info.json +3 -3
- package/dist/services/code-checks.js +27 -0
- package/dist/services/vnext-merge.js +4 -3
- package/package.json +1 -1
package/CHANGELOG.md
CHANGED
|
@@ -1,5 +1,11 @@
|
|
|
1
1
|
# @deksden-com/dd-flow-cli
|
|
2
2
|
|
|
3
|
+
## 0.9.0-beta.115
|
|
4
|
+
|
|
5
|
+
### Patch Changes
|
|
6
|
+
|
|
7
|
+
- Freeze the reviewed source check profile for MERGE after verifying that it only adds aliases to the integration baseline; keep existing check policy immutable and verify the post-apply profile against that frozen source.
|
|
8
|
+
|
|
3
9
|
## 0.9.0-beta.114
|
|
4
10
|
|
|
5
11
|
### Patch Changes
|
package/dist/build-info.json
CHANGED
|
@@ -1,8 +1,8 @@
|
|
|
1
1
|
{
|
|
2
2
|
"cli_package": "@deksden-com/dd-flow-cli",
|
|
3
|
-
"cli_version": "0.9.0-beta.
|
|
4
|
-
"cli_commit": "
|
|
5
|
-
"built_at": "2026-09-27T17:
|
|
3
|
+
"cli_version": "0.9.0-beta.115",
|
|
4
|
+
"cli_commit": "c9825bc3272340c5583cab59ccdaab47c9968b10",
|
|
5
|
+
"built_at": "2026-09-27T17:57:38.414Z",
|
|
6
6
|
"built_with_canon": {
|
|
7
7
|
"version": "4.1.1",
|
|
8
8
|
"commit": "678daa038287c948ada5b2d785a6dcc925c7b891",
|
|
@@ -3,6 +3,7 @@ import { isGitlessWorkspace, ownedWorkspaceHookPath, projectServicePathFilter, w
|
|
|
3
3
|
import { spawn, spawnSync } from "node:child_process";
|
|
4
4
|
import fs from "node:fs";
|
|
5
5
|
import path from "node:path";
|
|
6
|
+
import { isDeepStrictEqual } from "node:util";
|
|
6
7
|
import { parse } from "shell-quote";
|
|
7
8
|
import { assertRunMutationAllowed } from "./run-recovery.js";
|
|
8
9
|
import { AppError } from "../shared/errors.js";
|
|
@@ -66,6 +67,32 @@ export function codeCheckProfileHash(workspaceRoot, checks = []) {
|
|
|
66
67
|
};
|
|
67
68
|
return crypto.createHash("sha256").update(JSON.stringify(canonical)).digest("hex");
|
|
68
69
|
}
|
|
70
|
+
/** The integration target starts at its baseline profile; the reviewed source
|
|
71
|
+
* may add aliases, but must not change existing check policy or bindings. */
|
|
72
|
+
export function mergeCheckProfileHash(sourceWorkspace, targetWorkspace, checks) {
|
|
73
|
+
const source = readCodeCheckProfile(sourceWorkspace).profile;
|
|
74
|
+
const target = readCodeCheckProfile(targetWorkspace).profile;
|
|
75
|
+
if (!source && !target)
|
|
76
|
+
return null;
|
|
77
|
+
if (!source || !target)
|
|
78
|
+
throw new AppError("check_profile_drift", "Source and integration check profiles differ", 1);
|
|
79
|
+
const retained = structuredClone(source);
|
|
80
|
+
for (const field of ["aliases", "ports_by_alias", "inputs_by_alias"]) {
|
|
81
|
+
const old = target[field];
|
|
82
|
+
if (!old) {
|
|
83
|
+
delete retained[field];
|
|
84
|
+
continue;
|
|
85
|
+
}
|
|
86
|
+
const current = source[field] ?? {};
|
|
87
|
+
retained[field] = Object.fromEntries(Object.keys(old).filter(key => key in current).map(key => [key, current[key]]));
|
|
88
|
+
}
|
|
89
|
+
const existingAliases = new Set(Object.keys(target.aliases));
|
|
90
|
+
const changedExistingMetadata = ["ports_by_alias", "inputs_by_alias"].some(field => Object.keys(source[field] ?? {}).some(alias => existingAliases.has(alias) && !(alias in (target[field] ?? {}))));
|
|
91
|
+
if (changedExistingMetadata || !isDeepStrictEqual(retained, target)) {
|
|
92
|
+
throw new AppError("check_profile_drift", "Source check profile changes existing integration policy", 1, { source_profile_hash: codeCheckProfileHash(sourceWorkspace, checks), target_profile_hash: codeCheckProfileHash(targetWorkspace, checks) });
|
|
93
|
+
}
|
|
94
|
+
return codeCheckProfileHash(sourceWorkspace, checks);
|
|
95
|
+
}
|
|
69
96
|
export function validateCodeCheckCommands(workspaceRoot, commands) {
|
|
70
97
|
const { profile, file } = readCodeCheckProfile(workspaceRoot);
|
|
71
98
|
return commands.map((command) => {
|
|
@@ -17,7 +17,7 @@ import { runtimeCommand, checkRetryArgs, recoveryEvidenceInput } from "./runtime
|
|
|
17
17
|
import { acceptStageReport, prepareStageReport, publishStageReport } from "./stage-report-renderer.js";
|
|
18
18
|
import { bindStageCoordinatorWork, createChildWork, failWork, finishFanInWork, finishWork, refreshRunWorkProjection, startStageCoordinatorWork } from "./work-registry.js";
|
|
19
19
|
import { addVnextCodeRepair } from "./vnext-code.js";
|
|
20
|
-
import { checkReceiptEvidenceValid, checkReceipts,
|
|
20
|
+
import { checkReceiptEvidenceValid, checkReceipts, effectiveCheckDeclarations, mergeCheckProfileHash, runCodeChecks, validateFailedCheckRetry, workspaceChangedPaths, workspaceFingerprint } from "./code-checks.js";
|
|
21
21
|
import { applyExternalStageContext, prepareExternalStageContext } from "./stage-context.js";
|
|
22
22
|
import { ensureLaneWorkspace, prepareLaneWorkspace, heartbeatLaneLock, releaseLaneLock, waitAcquireLaneLock } from "./lanes.js";
|
|
23
23
|
import { assertRunMutationAllowed } from "./run-recovery.js";
|
|
@@ -71,7 +71,8 @@ export function prepareVnextMergeRequest(context, input) {
|
|
|
71
71
|
const freezeFile = path.join(freezeRoot, "source-freeze.json");
|
|
72
72
|
const gateFile = path.join(freezeRoot, "merge-gate.json");
|
|
73
73
|
fs.mkdirSync(freezeRoot, { recursive: true });
|
|
74
|
-
|
|
74
|
+
const profileHash = mergeCheckProfileHash(run.workspace_root, targetWorkspace, effective);
|
|
75
|
+
freezeMergeGate(gateFile, { runId: run.id, protocols, checks: effective, acceptanceRefs, profileHash, now: context.now() });
|
|
75
76
|
let sourceCommit;
|
|
76
77
|
let sourcePaths;
|
|
77
78
|
if (fs.existsSync(freezeFile)) {
|
|
@@ -109,7 +110,7 @@ export function prepareVnextMergeRequest(context, input) {
|
|
|
109
110
|
return requestView(context, raced);
|
|
110
111
|
const child = createChildWork(context, { parentWorkId: root.work_id, stage: "merge", slug: "merge", task: `Integrate frozen source ${sourceCommit} into ${targetBranch}, resolve material conflicts, and preserve accepted behavior.`, launchPolicy: route === "server" ? "fresh_agent_required" : "reuse_allowed", resultSchema: "dd-flow/merge-result@1", payload: { kind: "merge", checks: semantic, protocol_ids: protocols } });
|
|
111
112
|
const requestId = nextMergeRequestId(context);
|
|
112
|
-
context.db.run("INSERT INTO merge_requests (merge_request_id, project_id, run_id, executor_work_id, protocol_ids_json, source_workspace, source_branch, source_commit, target_workspace, target_branch, enqueue_target_head, execution_target_head, integration_commit, execution_route, status, dispatch_owner, dispatch_lease_token, dispatch_lease_expires_at, lock_acquired_at, checkpoint, profile_hash, replacement_of_merge_request_id, adapter_receipt_json, result_json, last_error_json, created_at, updated_at, completed_at) VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, NULL, NULL, ?, 'queued', NULL, NULL, NULL, NULL, 'queued', ?, ?, NULL, NULL, NULL, ?, ?, NULL)", [requestId, project.id, run.id, child.work_id, JSON.stringify(protocols), run.workspace_root, sourceBranch, sourceCommit, targetWorkspace, targetBranch, enqueueTarget, route,
|
|
113
|
+
context.db.run("INSERT INTO merge_requests (merge_request_id, project_id, run_id, executor_work_id, protocol_ids_json, source_workspace, source_branch, source_commit, target_workspace, target_branch, enqueue_target_head, execution_target_head, integration_commit, execution_route, status, dispatch_owner, dispatch_lease_token, dispatch_lease_expires_at, lock_acquired_at, checkpoint, profile_hash, replacement_of_merge_request_id, adapter_receipt_json, result_json, last_error_json, created_at, updated_at, completed_at) VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, NULL, NULL, ?, 'queued', NULL, NULL, NULL, NULL, 'queued', ?, ?, NULL, NULL, NULL, ?, ?, NULL)", [requestId, project.id, run.id, child.work_id, JSON.stringify(protocols), run.workspace_root, sourceBranch, sourceCommit, targetWorkspace, targetBranch, enqueueTarget, route, profileHash, replacementOf(run), now, now]);
|
|
113
114
|
appendFlowRunTimelineEvent(context, project.id, run.id, { type: "merge_request_queued", merge_request_id: requestId, work_id: child.work_id, route, source_commit: sourceCommit, target_branch: targetBranch });
|
|
114
115
|
return requestView(context, requireRequest(context, requestId));
|
|
115
116
|
});
|