@deksden-com/dd-flow-cli 0.9.0-beta.144 → 0.9.0-beta.145
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 +59 -0
- package/README.md +13 -8
- package/dist/build-info.json +7 -7
- package/dist/cli/command-inputs.js +9 -3
- package/dist/cli/command-metadata.js +33 -14
- package/dist/cli/help.js +32 -1
- package/dist/cli/hook-ingress.js +27 -3
- package/dist/cli/memory-flow-admission.js +7 -3
- package/dist/cli/memory-init-invocation.js +10 -3
- package/dist/cli/memory-init.js +74 -68
- package/dist/cli/memory-storage-admission.js +3 -2
- package/dist/cli/memory-storage-qualification.js +105 -0
- package/dist/cli/memory-storage-transition.js +123 -0
- package/dist/cli/memory-upgrade.js +189 -0
- package/dist/cli/native-hook-ingress.js +14 -4
- package/dist/cli/native-hook-preparation.js +55 -0
- package/dist/cli/response-file.js +2 -1
- package/dist/cli/run-cli.js +70 -32
- package/dist/cli/setup.js +2 -1
- package/dist/cli.js +12 -0
- package/dist/domain/stage-handler-identities.js +8 -0
- package/dist/harness-runtime/bin/dd-agy.mjs +3 -3
- package/dist/harness-runtime/bin/dd-grok.mjs +3 -3
- package/dist/harness-runtime/bin/dd-zcode.mjs +5 -5
- package/dist/harness-runtime/lib/agy-error-boundary.mjs +58 -0
- package/dist/harness-runtime/lib/dd-agy-daemon.mjs +62 -20
- package/dist/harness-runtime/lib/dd-agy.mjs +17 -3
- package/dist/harness-runtime/lib/dd-droid.mjs +1 -1
- package/dist/harness-runtime/lib/dd-grok-daemon.mjs +70 -13
- package/dist/harness-runtime/lib/dd-grok.mjs +6 -3
- package/dist/harness-runtime/lib/dd-zcode.mjs +44 -14
- package/dist/harness-runtime/lib/delegation-instructions.mjs +1 -1
- package/dist/harness-runtime/lib/entity-references.d.mts +2 -0
- package/dist/harness-runtime/lib/entity-references.mjs +9 -0
- package/dist/harness-runtime/lib/managed-daemon.mjs +13 -6
- package/dist/harness-runtime/lib/native-hook-command.mjs +4 -4
- package/dist/harness-runtime/lib/opencode-plugin.mjs +3 -3
- package/dist/harness-runtime/lib/operation-errors.mjs +1 -1
- package/dist/harness-runtime/lib/provider-overload-policy.d.mts +4 -2
- package/dist/harness-runtime/lib/provider-overload-policy.mjs +19 -6
- package/dist/harness-runtime/lib/runner-events.mjs +3 -1
- package/dist/harness-runtime/lib/setup-progress.mjs +8 -6
- package/dist/schemas/mb-upgrade-stage-result.schema.json +863 -0
- package/dist/schemas/memory-repair-work-result.schema.json +9 -2
- package/dist/schemas/release-impact.schema.json +3 -3
- package/dist/schemas/stage-start-response.schema.json +4 -0
- package/dist/services/accepted-intent.js +13 -1
- package/dist/services/canon.js +43 -7
- package/dist/services/cli-operation-classifier.js +1 -1
- package/dist/services/cli-upgrade-allowlist.js +1 -1
- package/dist/services/code-checks.js +11 -4
- package/dist/services/compatibility-preflight.js +5 -2
- package/dist/services/controller-fanout.js +21 -4
- package/dist/services/controller-memory-task.js +45 -0
- package/dist/services/controller-memory-worker.js +48 -0
- package/dist/services/dashboard.js +2 -2
- package/dist/services/descriptor-run-start.js +26 -19
- package/dist/services/descriptor-stage-coordinator.js +30 -10
- package/dist/services/descriptor-stage-start.js +40 -4
- package/dist/services/engines.js +64 -14
- package/dist/services/eval-snapshots.js +104 -93
- package/dist/services/external-work-launch.js +5 -3
- package/dist/services/flow-catalog.js +8 -4
- package/dist/services/frozen-engine.js +113 -11
- package/dist/services/hook-memory-routing.js +188 -0
- package/dist/services/hook-recovery-guard.js +14 -0
- package/dist/services/hook-session-observation.js +24 -0
- package/dist/services/hooks.js +27 -8
- package/dist/services/hybrid-contracts.js +2 -13
- package/dist/services/hybrid-digest.js +14 -0
- package/dist/services/installed-flow-source.js +25 -4
- package/dist/services/lifecycle-invocations.js +33 -5
- package/dist/services/memory-accepted-composition.js +5 -4
- package/dist/services/memory-completed-correction-wave.js +23 -15
- package/dist/services/memory-compose-handler.js +163 -21
- package/dist/services/memory-composition-acceptance.js +186 -10
- package/dist/services/memory-composition-graph.js +42 -11
- package/dist/services/memory-composition-registration.js +23 -8
- package/dist/services/memory-composition-work-custody.js +14 -9
- package/dist/services/memory-composition-work-results.js +15 -3
- package/dist/services/memory-corrected-review-batch.js +10 -3
- package/dist/services/memory-corrected-review-findings.js +4 -2
- package/dist/services/memory-corrected-review-inputs.js +10 -5
- package/dist/services/memory-corrected-review-receipt.js +17 -9
- package/dist/services/memory-corrected-review-registration.js +3 -2
- package/dist/services/memory-correction-check-receipt.js +20 -4
- package/dist/services/memory-custody-snapshot.js +36 -0
- package/dist/services/memory-finish-evidence.js +44 -22
- package/dist/services/memory-finish-handler.js +37 -23
- package/dist/services/memory-finish-integration.js +218 -48
- package/dist/services/memory-finish-readiness.js +66 -19
- package/dist/services/memory-git-integration.js +87 -18
- package/dist/services/memory-historical-corrected-review.js +21 -15
- package/dist/services/memory-historical-original-review.js +22 -9
- package/dist/services/memory-historical-repair-registration.js +14 -8
- package/dist/services/memory-historical-second-review.js +30 -11
- package/dist/services/memory-init-contexts.js +28 -2
- package/dist/services/memory-init-controller-start.js +19 -8
- package/dist/services/memory-init-git-policy.js +50 -21
- package/dist/services/memory-init-lint-capability.js +21 -3
- package/dist/services/memory-init-native-capacity.js +13 -0
- package/dist/services/memory-init-preflight.js +11 -1
- package/dist/services/memory-init-request-candidate.js +73 -17
- package/dist/services/memory-init-request-input.js +23 -7
- package/dist/services/memory-init-run-bundle.js +49 -12
- package/dist/services/memory-init-storage.js +17 -7
- package/dist/services/memory-layer.js +68 -4
- package/dist/services/memory-lint-config.js +111 -2
- package/dist/services/memory-native-review-copy.js +5 -67
- package/dist/services/memory-normal-flow-entry.js +12 -0
- package/dist/services/memory-ordinary-defaults.js +22 -5
- package/dist/services/memory-preflight-handler.js +15 -0
- package/dist/services/memory-repair-current-view.js +1 -1
- package/dist/services/memory-repair-document-delta.js +70 -33
- package/dist/services/memory-repair-history.js +12 -7
- package/dist/services/memory-repair-physical-binding.js +1 -119
- package/dist/services/memory-repair-publication-plan.js +5 -3
- package/dist/services/memory-repair-publication-source.js +1 -1
- package/dist/services/memory-repair-publication.js +3 -2
- package/dist/services/memory-repair-registration.js +2 -1
- package/dist/services/memory-repair-work-baseline.js +4 -2
- package/dist/services/memory-repair-work-completion.js +32 -12
- package/dist/services/memory-repair-work-results.js +21 -2
- package/dist/services/memory-repair-workspace.js +11 -2
- package/dist/services/memory-repair-writer-copy.js +2 -1
- package/dist/services/memory-result-binding.js +78 -12
- package/dist/services/memory-retained-review-authority.js +17 -8
- package/dist/services/memory-retained-second-baseline.js +1 -1
- package/dist/services/memory-retained-second-repairs.js +12 -6
- package/dist/services/memory-review-correction-plan.js +47 -12
- package/dist/services/memory-review-handler.js +17 -17
- package/dist/services/memory-review-inputs.js +55 -9
- package/dist/services/memory-review-limits.js +10 -0
- package/dist/services/memory-review-receipt.js +2 -2
- package/dist/services/memory-review-registration.js +13 -4
- package/dist/services/memory-review-work-custody.js +3 -1
- package/dist/services/memory-review-work-input.js +26 -7
- package/dist/services/memory-review-work-results.js +26 -4
- package/dist/services/memory-second-corrected-review-inputs.js +5 -3
- package/dist/services/memory-second-correction-plan.js +2 -1
- package/dist/services/memory-second-repair-launch.js +4 -4
- package/dist/services/memory-second-repair-source.js +1 -1
- package/dist/services/memory-stage-dispatch.js +38 -1
- package/dist/services/memory-stage-results.js +113 -22
- package/dist/services/memory-upgrade-analysis-handler.js +243 -0
- package/dist/services/memory-upgrade-bootstrap-readiness.js +251 -0
- package/dist/services/memory-upgrade-evidence.js +100 -0
- package/dist/services/memory-upgrade-inventory.js +442 -0
- package/dist/services/memory-upgrade-publication-custody.js +73 -0
- package/dist/services/memory-upgrade-publication.js +413 -0
- package/dist/services/memory-upgrade-selection.js +430 -0
- package/dist/services/memory-upgrade-start.js +60 -0
- package/dist/services/memory-upgrade-storage-readiness.js +340 -0
- package/dist/services/memory-verification-acceptance.js +38 -17
- package/dist/services/memory-verification-checks.js +77 -23
- package/dist/services/memory-verification-receipt.js +6 -5
- package/dist/services/memory-verify-handler.js +15 -13
- package/dist/services/memory-work-result-json.js +12 -0
- package/dist/services/memory-work-result-preparation.js +5 -4
- package/dist/services/merge-server.js +4 -2
- package/dist/services/migrations.js +127 -98
- package/dist/services/physical-startup.js +45 -0
- package/dist/services/project-registry.js +97 -0
- package/dist/services/project-summary.js +1 -1
- package/dist/services/projects.js +3 -93
- package/dist/services/prompt-delivery.js +71 -0
- package/dist/services/provider-capacity.js +69 -8
- package/dist/services/recovery-snapshot-database.js +32 -4
- package/dist/services/repair-intents.js +1 -12
- package/dist/services/repair-receipt.js +13 -0
- package/dist/services/run-control-worker.js +35 -18
- package/dist/services/run-controller-process.js +2 -6
- package/dist/services/run-controller-recovery.js +6 -1
- package/dist/services/run-controller.js +131 -80
- package/dist/services/run-recovery-guard.js +30 -0
- package/dist/services/run-recovery.js +11 -44
- package/dist/services/run-start-custody.js +17 -5
- package/dist/services/runtime-scope-worker.js +46 -19
- package/dist/services/sessions.js +3 -24
- package/dist/services/setup.js +63 -13
- package/dist/services/stage-handlers.js +9 -7
- package/dist/services/stage-pause.js +108 -44
- package/dist/services/version-status.js +1 -1
- package/dist/services/vnext-fanout.js +21 -9
- package/dist/services/vnext-plan-review.js +3 -5
- package/dist/services/work-registry.js +80 -33
- package/dist/services/workspace-files.js +1 -1
- package/dist/shared/entity-references.js +1 -8
- package/dist/storage/data-preservation.js +65 -0
- package/dist/storage/database.js +53 -19
- package/dist/storage/import-guard.js +84 -27
- package/dist/storage/memory-upgrade-schema-bootstrap.js +38 -0
- package/dist/storage/paired-writer-migration.js +424 -0
- package/dist/storage/writer-contract.js +11 -4
- package/dist/storage/writer-migration.js +313 -52
- package/dist/storage/writer-restore.js +217 -0
- package/package.json +8 -4
|
@@ -1,4 +1,5 @@
|
|
|
1
1
|
import path from "node:path";
|
|
2
|
+
import fs from "node:fs";
|
|
2
3
|
import { spawnSync } from "node:child_process";
|
|
3
4
|
import { AppError } from "../shared/errors.js";
|
|
4
5
|
import { hybridDigest } from "./hybrid-contracts.js";
|
|
@@ -15,6 +16,51 @@ function validScope(scope) {
|
|
|
15
16
|
|| Object.keys(scope).sort().join() !== "projectRoot,requestId,workspaceRoot")
|
|
16
17
|
refuse("invalid_request_scope");
|
|
17
18
|
}
|
|
19
|
+
/** Shared read-only policy observation for init and upgrade. The caller owns
|
|
20
|
+
* retained input custody; these facts grant neither dispatch nor push. */
|
|
21
|
+
export function observeMemoryGitPolicySelection(environment, input) {
|
|
22
|
+
validScope(input.scope);
|
|
23
|
+
const { scope, selection } = input;
|
|
24
|
+
if (!selection || Object.keys(selection).sort().join() !== "integrationBranch,route"
|
|
25
|
+
|| !["integration_branch_direct", "feature_worktree"].includes(selection.route)
|
|
26
|
+
|| typeof selection.integrationBranch !== "string" || !selection.integrationBranch || selection.integrationBranch.startsWith("-") || selection.integrationBranch.length > 1024
|
|
27
|
+
|| [...selection.integrationBranch].some(character => character.charCodeAt(0) <= 32 || character.charCodeAt(0) === 127))
|
|
28
|
+
refuse("explicit_policy_required");
|
|
29
|
+
if (typeof input.gitExecutable !== "string" || !path.isAbsolute(input.gitExecutable))
|
|
30
|
+
refuse("explicit_git_required");
|
|
31
|
+
const env = Object.fromEntries(Object.entries(environment).filter(([key]) => !key.startsWith("GIT_")));
|
|
32
|
+
const git = (directory, args, optional = false) => {
|
|
33
|
+
const result = spawnSync(input.gitExecutable, ["-c", "core.fsmonitor=false", "-C", directory, ...args], { env: { ...env, GIT_OPTIONAL_LOCKS: "0" }, encoding: "utf8", timeout: 5000, maxBuffer: 64 * 1024, shell: false, windowsHide: true });
|
|
34
|
+
if (result.error || result.signal || !optional && result.status !== 0)
|
|
35
|
+
refuse("git_policy_observation_failed");
|
|
36
|
+
return result.status === 0 ? result.stdout.trim() : null;
|
|
37
|
+
};
|
|
38
|
+
const valid = spawnSync(input.gitExecutable, ["check-ref-format", `refs/heads/${selection.integrationBranch}`], { env: { ...env, GIT_OPTIONAL_LOCKS: "0" }, timeout: 5000, maxBuffer: 64 * 1024, shell: false, windowsHide: true });
|
|
39
|
+
if (valid.error || valid.signal || valid.status !== 0)
|
|
40
|
+
refuse("invalid_integration_branch");
|
|
41
|
+
const common = (directory) => {
|
|
42
|
+
if (git(directory, ["rev-parse", "--is-bare-repository"]) !== "false"
|
|
43
|
+
|| fs.realpathSync(git(directory, ["rev-parse", "--show-toplevel"])) !== directory)
|
|
44
|
+
refuse("not_repository_root");
|
|
45
|
+
return fs.realpathSync(path.resolve(directory, git(directory, ["rev-parse", "--git-common-dir"])));
|
|
46
|
+
};
|
|
47
|
+
const repository = common(scope.projectRoot);
|
|
48
|
+
if (common(scope.workspaceRoot) !== repository)
|
|
49
|
+
refuse("foreign_workspace_repository");
|
|
50
|
+
const branch = git(scope.workspaceRoot, ["symbolic-ref", "--short", "HEAD"]);
|
|
51
|
+
if (!branch)
|
|
52
|
+
refuse("workspace_branch_unavailable");
|
|
53
|
+
if (selection.route === "integration_branch_direct"
|
|
54
|
+
? scope.projectRoot !== scope.workspaceRoot || branch !== selection.integrationBranch
|
|
55
|
+
: scope.projectRoot === scope.workspaceRoot || branch === selection.integrationBranch)
|
|
56
|
+
refuse("route_workspace_mismatch");
|
|
57
|
+
if (selection.route === "feature_worktree"
|
|
58
|
+
&& !/^[a-f0-9]{40,64}$/.test(git(scope.projectRoot, ["rev-parse", "--verify", `refs/heads/${selection.integrationBranch}^{commit}`], true) ?? ""))
|
|
59
|
+
refuse("integration_branch_unavailable");
|
|
60
|
+
const head = git(scope.workspaceRoot, ["rev-parse", "--verify", "HEAD"], true);
|
|
61
|
+
return { repository, workspace_branch: branch, initial_head: head, source: "explicit_request",
|
|
62
|
+
route: selection.route, integration_branch: selection.integrationBranch, push_authorized: false };
|
|
63
|
+
}
|
|
18
64
|
/** SQL preservation, not fresh policy inference, Git execution or permission. */
|
|
19
65
|
export function readMemoryInitGitPolicy(context, scope) {
|
|
20
66
|
validScope(scope);
|
|
@@ -75,32 +121,15 @@ export class PreparedMemoryInitGitPolicy {
|
|
|
75
121
|
static prepareFresh(environment, input) {
|
|
76
122
|
if (!(input.target instanceof PreparedMemoryInitTarget))
|
|
77
123
|
refuse("prepared_target_required");
|
|
78
|
-
const selection = input.selection;
|
|
79
|
-
if (!selection || Object.keys(selection).sort().join() !== "integrationBranch,route"
|
|
80
|
-
|| !["integration_branch_direct", "feature_worktree"].includes(selection.route)
|
|
81
|
-
|| typeof selection.integrationBranch !== "string" || !selection.integrationBranch || selection.integrationBranch.startsWith("-") || selection.integrationBranch.length > 1024
|
|
82
|
-
|| [...selection.integrationBranch].some(character => character.charCodeAt(0) <= 32 || character.charCodeAt(0) === 127))
|
|
83
|
-
refuse("explicit_policy_required");
|
|
84
124
|
input.target.revalidate();
|
|
85
125
|
const facts = input.target.facts(), scope = { requestId: facts.request_id, projectRoot: facts.project_root, workspaceRoot: facts.workspace_root };
|
|
86
126
|
if (!facts.tool_probes)
|
|
87
127
|
refuse("fresh_target_required");
|
|
88
|
-
const
|
|
89
|
-
|
|
90
|
-
|
|
91
|
-
refuse("invalid_integration_branch");
|
|
92
|
-
if (selection.route === "integration_branch_direct"
|
|
93
|
-
? scope.projectRoot !== scope.workspaceRoot || facts.branch !== selection.integrationBranch
|
|
94
|
-
: scope.projectRoot === scope.workspaceRoot || facts.branch === selection.integrationBranch)
|
|
95
|
-
refuse("route_workspace_mismatch");
|
|
96
|
-
if (selection.route === "feature_worktree") {
|
|
97
|
-
const branch = spawnSync(facts.tools.git, ["-C", scope.projectRoot, "rev-parse", "--verify", `refs/heads/${selection.integrationBranch}^{commit}`], { env: { ...env, GIT_OPTIONAL_LOCKS: "0" }, encoding: "utf8", timeout: 5000, maxBuffer: 64 * 1024, shell: false, windowsHide: true });
|
|
98
|
-
if (branch.error || branch.signal || branch.status !== 0 || !/^[a-f0-9]{40,64}$/.test(branch.stdout.trim()))
|
|
99
|
-
refuse("integration_branch_unavailable");
|
|
100
|
-
}
|
|
128
|
+
const policy = observeMemoryGitPolicySelection(environment, { scope, gitExecutable: facts.tools.git, selection: input.selection });
|
|
129
|
+
if (policy.repository !== facts.repository || policy.workspace_branch !== facts.branch || policy.initial_head !== facts.head)
|
|
130
|
+
refuse("target_changed_after_preparation");
|
|
101
131
|
const receipt = { schema_id: "dd-flow/memory-init-git-policy@1", scope, target_sha256: input.target.sha256,
|
|
102
|
-
|
|
103
|
-
route: selection.route, integration_branch: selection.integrationBranch, push_authorized: false };
|
|
132
|
+
...policy };
|
|
104
133
|
return new PreparedMemoryInitGitPolicy(token, input.target, receipt);
|
|
105
134
|
}
|
|
106
135
|
value() { return structuredClone(this.#receipt); }
|
|
@@ -54,6 +54,12 @@ export class PreparedMemoryInitLintCapability {
|
|
|
54
54
|
const selected = target.facts(), executable = selected.tools["mb-lint"], probe = selected.tool_probes?.["mb-lint"];
|
|
55
55
|
if (!probe)
|
|
56
56
|
refuse("missing_selected_tool_probe");
|
|
57
|
+
return PreparedMemoryInitLintCapability.prepareExecutable(executable, probe, env);
|
|
58
|
+
}
|
|
59
|
+
static prepareExecutable(executable, probe, env, options = {}) {
|
|
60
|
+
if (!path.isAbsolute(executable) || !probe || !/^[a-f0-9]{64}$/.test(probe.executable_sha256)
|
|
61
|
+
|| !/^\d+\.\d+\.\d+(?:-[0-9A-Za-z.-]+)?(?:\+[0-9A-Za-z.-]+)?$/.test(probe.version))
|
|
62
|
+
refuse("invalid_selected_tool");
|
|
57
63
|
const checksum = () => {
|
|
58
64
|
try {
|
|
59
65
|
return hash(readBoundedReleaseFile(executable, 64 * 1024 * 1024));
|
|
@@ -97,11 +103,11 @@ export class PreparedMemoryInitLintCapability {
|
|
|
97
103
|
}
|
|
98
104
|
};
|
|
99
105
|
const probeEnv = Object.fromEntries(Object.entries(env).filter(([key]) => !key.startsWith("GIT_")));
|
|
100
|
-
const run = (status, rule) => {
|
|
106
|
+
const run = (status, rule, config) => {
|
|
101
107
|
ownDirectories();
|
|
102
|
-
const result = spawnSync(executable, ["--root", scratch, "--files", logical, "--format", "json"], { cwd: scratch, env: { ...probeEnv, GIT_OPTIONAL_LOCKS: "0" }, shell: false, windowsHide: true, timeout: 5000, maxBuffer: 256 * 1024 });
|
|
108
|
+
const result = spawnSync(executable, ["--root", scratch, "--files", logical, "--format", "json", ...(config ? ["--config", config] : [])], { cwd: scratch, env: { ...probeEnv, GIT_OPTIONAL_LOCKS: "0" }, shell: false, windowsHide: true, timeout: 5000, maxBuffer: 256 * 1024 });
|
|
103
109
|
if (result.error || result.signal || result.status !== status)
|
|
104
|
-
refuse("lint_probe_execution_failed");
|
|
110
|
+
refuse(config ? "temporary_configuration_not_supported" : "lint_probe_execution_failed");
|
|
105
111
|
ownDirectories();
|
|
106
112
|
const actual = fs.lstatSync(file);
|
|
107
113
|
if (!actual.isFile() || actual.nlink !== 1 || !same(actual, fileOwner)
|
|
@@ -136,6 +142,15 @@ export class PreparedMemoryInitLintCapability {
|
|
|
136
142
|
const fields = run(1, "frontmatter/required-fields");
|
|
137
143
|
writeProbe(`${validDocument}\n[Missing target](missing-probe-target.md)\n`);
|
|
138
144
|
const links = run(1, "links/markdown-target-exists");
|
|
145
|
+
if (options.temporaryConfiguration) {
|
|
146
|
+
// Upgrade's active-document profile needs an external config with exact
|
|
147
|
+
// finding exclusions; a version response or basic JSON lint is insufficient.
|
|
148
|
+
const config = path.join(scratch, "upgrade-lint.json");
|
|
149
|
+
fs.writeFileSync(config, JSON.stringify({ ignoreFindings: [{
|
|
150
|
+
ruleId: "links/markdown-target-exists", file: logical, target: "missing-probe-target.md"
|
|
151
|
+
}] }), { flag: "wx", mode: 0o600 });
|
|
152
|
+
run(0, undefined, config);
|
|
153
|
+
}
|
|
139
154
|
if (checksum() !== probe.executable_sha256)
|
|
140
155
|
refuse("selected_executable_changed");
|
|
141
156
|
prepared = new PreparedMemoryInitLintCapability({ schema_id: "dd-flow/memory-init-lint-capability@1",
|
|
@@ -170,6 +185,9 @@ export class PreparedMemoryInitLintCapability {
|
|
|
170
185
|
if (!probe || selected.tools["mb-lint"] !== this.#facts.executable
|
|
171
186
|
|| probe.version !== this.#facts.version || probe.executable_sha256 !== this.#facts.executable_sha256)
|
|
172
187
|
refuse("lint_capability_changed");
|
|
188
|
+
this.revalidateExecutable();
|
|
189
|
+
}
|
|
190
|
+
revalidateExecutable() {
|
|
173
191
|
// This instance already passed the functional probes. Publication checks
|
|
174
192
|
// preserve its selected executable, not repeat qualification after effects.
|
|
175
193
|
let checksum;
|
|
@@ -12,6 +12,7 @@ import { readBoundedReleaseFile } from "./release-source.js";
|
|
|
12
12
|
import { acquireInstallationLock } from "./installation-lock.js";
|
|
13
13
|
import { inspectDaemonOperation } from "../harness-runtime/lib/daemon-operations.mjs";
|
|
14
14
|
import { nativeCommandFingerprint } from "../harness-runtime/lib/native-request-proof.mjs";
|
|
15
|
+
import { validateMemoryInitStartupScope } from "./memory-init-startup-custody.js";
|
|
15
16
|
import { continueProviderCapacity } from "./provider-capacity.js";
|
|
16
17
|
import { CAPACITY_POLICY, MAX_OVERLOAD_RETRIES, refusalObservation, terminalProviderOverload } from "../harness-runtime/lib/provider-overload-policy.mjs";
|
|
17
18
|
const sha = (bytes) => createHash("sha256").update(bytes).digest("hex");
|
|
@@ -127,6 +128,8 @@ export async function produceMemoryInitNativeCapacity(input) {
|
|
|
127
128
|
};
|
|
128
129
|
if (result)
|
|
129
130
|
return await publish(result);
|
|
131
|
+
if (input.restoreOnly)
|
|
132
|
+
refuse("completed_probe_required", "unknown");
|
|
130
133
|
// An independent bounded technical workspace cannot modify the target.
|
|
131
134
|
const workspace = path.join(root, "project"), stateDir = path.join(root, "daemon");
|
|
132
135
|
fs.mkdirSync(workspace, { recursive: true, mode: 0o700 });
|
|
@@ -294,3 +297,13 @@ export async function produceMemoryInitNativeCapacity(input) {
|
|
|
294
297
|
lease.release();
|
|
295
298
|
}
|
|
296
299
|
}
|
|
300
|
+
/** Recover exact completed probe bytes for a retained raw request whose
|
|
301
|
+
* resolved selection was not published. Never starts or repairs a probe. */
|
|
302
|
+
export async function restoreCompletedMemoryNativeCapacity(input) {
|
|
303
|
+
validateMemoryInitStartupScope(input.scope);
|
|
304
|
+
const prior = read(path.join(input.home, "memory-init-native-probes", hybridDigest(input.scope), "input.json"));
|
|
305
|
+
if (!prior || hybridDigest(prior.scope) !== hybridDigest(input.scope))
|
|
306
|
+
refuse("original_probe_required", "unknown");
|
|
307
|
+
return produceMemoryInitNativeCapacity({ ...input, profile: prior.profile, maximum: Number(prior.maximum),
|
|
308
|
+
nativeConfigHome: String(prior.native_config_home), requestSha256: String(prior.request_sha256), allowLiveProbe: true, restoreOnly: true });
|
|
309
|
+
}
|
|
@@ -55,7 +55,7 @@ function noMemoryBank(directory) {
|
|
|
55
55
|
function probeTools(tools, env) {
|
|
56
56
|
const facts = Object.create(null);
|
|
57
57
|
const probeEnv = Object.fromEntries(Object.entries(env).filter(([key]) => !key.startsWith("GIT_")));
|
|
58
|
-
for (const name of
|
|
58
|
+
for (const name of Object.keys(tools)) {
|
|
59
59
|
const diagnostics = { name, executable: tools[name], args: ["--version"], timeout_ms: 5000, phase: "executable_read" };
|
|
60
60
|
try {
|
|
61
61
|
const digest = () => createHash("sha256").update(readBoundedReleaseFile(tools[name], 64 * 1024 * 1024)).digest("hex");
|
|
@@ -90,6 +90,16 @@ function probeTools(tools, env) {
|
|
|
90
90
|
}
|
|
91
91
|
return facts;
|
|
92
92
|
}
|
|
93
|
+
/** Shared deterministic executable selection; no project initialization or
|
|
94
|
+
* tool installation. Upgrade freezes this before content synchronization. */
|
|
95
|
+
export function inspectMemoryLintExecutable(env) {
|
|
96
|
+
const selected = executable("mb-lint", env);
|
|
97
|
+
return { executable: selected, probe: probeTools({ "mb-lint": selected }, env)["mb-lint"] };
|
|
98
|
+
}
|
|
99
|
+
export function inspectMemoryGitExecutable(env) {
|
|
100
|
+
const selected = executable("git", env);
|
|
101
|
+
return { executable: selected, probe: probeTools({ git: selected }, env).git };
|
|
102
|
+
}
|
|
93
103
|
function inspect(input, env, ownedPublication = false, qualifyTools = true, observe, lintReservation, sourceTools) {
|
|
94
104
|
if (!/^[A-Za-z0-9][A-Za-z0-9_.-]{0,95}$/.test(input.requestId))
|
|
95
105
|
refuse("invalid_request_id");
|
|
@@ -7,15 +7,21 @@ import { assertImportAdmission } from "../storage/import-guard.js";
|
|
|
7
7
|
import { hybridDigest } from "./hybrid-contracts.js";
|
|
8
8
|
import { readBoundedReleaseFile } from "./release-source.js";
|
|
9
9
|
import { validateMemoryInitObjective, validateMemoryInitStartupScope } from "./memory-init-startup-custody.js";
|
|
10
|
-
import { prepareMemoryInitRequestInput } from "./memory-init-request-input.js";
|
|
10
|
+
import { prepareMemoryInitRequestInput, prepareMemoryUpgradeRequestInput } from "./memory-init-request-input.js";
|
|
11
11
|
const preparedCandidates = new WeakMap();
|
|
12
12
|
/** Only a genuinely retained candidate can supply fresh startup input. Copied
|
|
13
13
|
* objects and caller-authored snapshots never acquire candidate authority. */
|
|
14
14
|
export function captureMemoryInitRequestCandidate(candidate, home, scope, objective) {
|
|
15
|
-
const
|
|
16
|
-
if (!
|
|
15
|
+
const witness = candidate && typeof candidate === "object" ? preparedCandidates.get(candidate) : undefined;
|
|
16
|
+
if (!witness || witness.operation !== "memory-init")
|
|
17
17
|
refuse("unprepared_request_candidate");
|
|
18
|
-
return capture(home, scope, objective);
|
|
18
|
+
return witness.capture(home, scope, objective);
|
|
19
|
+
}
|
|
20
|
+
export function captureMemoryUpgradeRequestCandidate(candidate, home, scope, objective) {
|
|
21
|
+
const witness = candidate && typeof candidate === "object" ? preparedCandidates.get(candidate) : undefined;
|
|
22
|
+
if (!witness || witness.operation !== "memory-upgrade")
|
|
23
|
+
refuse("unprepared_request_candidate");
|
|
24
|
+
return witness.capture(home, scope, objective);
|
|
19
25
|
}
|
|
20
26
|
const sha = (bytes) => createHash("sha256").update(bytes).digest("hex");
|
|
21
27
|
function refuse(reason) {
|
|
@@ -47,10 +53,42 @@ function syncDirectory(file) {
|
|
|
47
53
|
fs.closeSync(fd);
|
|
48
54
|
}
|
|
49
55
|
}
|
|
56
|
+
const candidateKey = (scope, operation) => hybridDigest(operation === "memory-upgrade"
|
|
57
|
+
? { projectRoot: scope.projectRoot, requestId: scope.requestId } : scope);
|
|
50
58
|
/** Read-only preparation and exact byte capture. A candidate is not startup,
|
|
51
59
|
* storage, engine or dispatch authority. Retain only after prerequisite checks;
|
|
52
60
|
* no missing candidate component is reconstructed from ambient selections. */
|
|
53
61
|
export function prepareMemoryInitRequestCandidate(home, scope, selection, intake) {
|
|
62
|
+
return prepareMemoryRequestCandidate(home, scope, selection, intake, "memory-init");
|
|
63
|
+
}
|
|
64
|
+
export function prepareMemoryUpgradeRequestCandidate(home, scope, selection, intake) {
|
|
65
|
+
return prepareMemoryRequestCandidate(home, scope, selection, intake, "memory-upgrade");
|
|
66
|
+
}
|
|
67
|
+
/** Exact original input recovery before an incompatible SQLite store is opened.
|
|
68
|
+
* No profile, installed release, model, provider or missing component is chosen. */
|
|
69
|
+
export function readMemoryUpgradeRequestCandidate(home, scope) {
|
|
70
|
+
validateMemoryInitStartupScope(scope);
|
|
71
|
+
directory(home);
|
|
72
|
+
const namespace = path.join(home, "memory-upgrade-request-candidates"), key = candidateKey(scope, "memory-upgrade");
|
|
73
|
+
if (!exists(namespace))
|
|
74
|
+
return null;
|
|
75
|
+
directory(namespace);
|
|
76
|
+
const root = path.join(namespace, key), marker = path.join(namespace, `${key}.json`);
|
|
77
|
+
if (!exists(root) && !exists(marker))
|
|
78
|
+
return null;
|
|
79
|
+
if (!exists(root) || !exists(marker))
|
|
80
|
+
refuse("candidate_custody_missing");
|
|
81
|
+
directory(root);
|
|
82
|
+
const selection = readBoundedReleaseFile(path.join(root, "selection.json"), 256 * 1024);
|
|
83
|
+
const intake = readBoundedReleaseFile(path.join(root, "intake.txt"), 64 * 1024);
|
|
84
|
+
const candidate = prepareMemoryUpgradeRequestCandidate(home, scope, selection, intake);
|
|
85
|
+
// Existing-candidate retain verifies only; it cannot fill partial publication.
|
|
86
|
+
if (candidate.retain().created)
|
|
87
|
+
refuse("candidate_custody_missing");
|
|
88
|
+
return { candidate, objective: candidate.objective,
|
|
89
|
+
input: captureMemoryUpgradeRequestCandidate(candidate, home, scope, candidate.objective) };
|
|
90
|
+
}
|
|
91
|
+
function prepareMemoryRequestCandidate(home, scope, selection, intake, operation) {
|
|
54
92
|
validateMemoryInitStartupScope(scope);
|
|
55
93
|
scope = structuredClone(scope);
|
|
56
94
|
const selectionBytes = Buffer.from(selection), intakeBytes = Buffer.from(intake);
|
|
@@ -64,17 +102,21 @@ export function prepareMemoryInitRequestCandidate(home, scope, selection, intake
|
|
|
64
102
|
return refuse("invalid_intake_utf8");
|
|
65
103
|
}
|
|
66
104
|
validateMemoryInitObjective(objective);
|
|
105
|
+
// Original-input validity is not installed-target or storage admission.
|
|
106
|
+
if (operation === "memory-upgrade")
|
|
107
|
+
prepareMemoryUpgradeRequestInput(scope, selectionBytes, intakeBytes, objective);
|
|
67
108
|
const homeOwner = directory(home);
|
|
68
109
|
if ([scope.projectRoot, scope.workspaceRoot].some(root => {
|
|
69
110
|
const relative = path.relative(root, home);
|
|
70
111
|
return relative === "" || !path.isAbsolute(relative) && relative !== ".." && !relative.startsWith(`..${path.sep}`);
|
|
71
112
|
}))
|
|
72
113
|
refuse("candidate_home_inside_project");
|
|
73
|
-
|
|
74
|
-
|
|
114
|
+
if (operation === "memory-init")
|
|
115
|
+
assertImportAdmission(home);
|
|
116
|
+
const namespace = path.join(home, `${operation}-request-candidates`), key = candidateKey(scope, operation);
|
|
75
117
|
const root = path.join(namespace, key), marker = path.join(namespace, `${key}.json`);
|
|
76
118
|
const inputFile = path.join(root, "intake.txt"), selectionFile = path.join(root, "selection.json");
|
|
77
|
-
const facts = { schema_id:
|
|
119
|
+
const facts = { schema_id: `dd-flow/${operation}-request-candidate@1`, home, home_owner: homeOwner, scope,
|
|
78
120
|
selection_sha256: sha(selectionBytes), intake_sha256: sha(intakeBytes) };
|
|
79
121
|
const verify = () => {
|
|
80
122
|
if (hybridDigest(directory(home)) !== hybridDigest(homeOwner))
|
|
@@ -84,7 +126,16 @@ export function prepareMemoryInitRequestCandidate(home, scope, selection, intake
|
|
|
84
126
|
refuse("candidate_custody_missing");
|
|
85
127
|
const expected = { ...facts, owner: directory(root) };
|
|
86
128
|
const bytes = readBoundedReleaseFile(marker, 16 * 1024);
|
|
87
|
-
|
|
129
|
+
let observed;
|
|
130
|
+
try {
|
|
131
|
+
observed = JSON.parse(bytes.toString("utf8"));
|
|
132
|
+
}
|
|
133
|
+
catch {
|
|
134
|
+
return refuse("candidate_request_changed");
|
|
135
|
+
}
|
|
136
|
+
// Metadata key order is not request identity. Original intake/selection
|
|
137
|
+
// remain byte-bound below; a reader must not rewrite their marker.
|
|
138
|
+
if (hybridDigest(observed) !== hybridDigest(expected))
|
|
88
139
|
refuse("candidate_request_changed");
|
|
89
140
|
if (!exists(inputFile) || !exists(selectionFile))
|
|
90
141
|
refuse("candidate_input_missing");
|
|
@@ -95,6 +146,10 @@ export function prepareMemoryInitRequestCandidate(home, scope, selection, intake
|
|
|
95
146
|
if (exists(namespace))
|
|
96
147
|
directory(namespace);
|
|
97
148
|
const found = exists(root) || exists(marker);
|
|
149
|
+
// Migration reconciliation must be able to read its original upgrade input
|
|
150
|
+
// while ordinary writers are fenced. It gains no permit or fresh write.
|
|
151
|
+
if (!found && operation === "memory-upgrade")
|
|
152
|
+
assertImportAdmission(home);
|
|
98
153
|
if (found)
|
|
99
154
|
verify();
|
|
100
155
|
let retained = false;
|
|
@@ -102,7 +157,7 @@ export function prepareMemoryInitRequestCandidate(home, scope, selection, intake
|
|
|
102
157
|
const candidate = {
|
|
103
158
|
objective,
|
|
104
159
|
retain: () => {
|
|
105
|
-
if (found) {
|
|
160
|
+
if (found || operation === "memory-upgrade" && retained) {
|
|
106
161
|
verify();
|
|
107
162
|
return complete(false);
|
|
108
163
|
}
|
|
@@ -145,13 +200,14 @@ export function prepareMemoryInitRequestCandidate(home, scope, selection, intake
|
|
|
145
200
|
}
|
|
146
201
|
}
|
|
147
202
|
};
|
|
148
|
-
preparedCandidates.set(candidate, (expectedHome, expectedScope, expectedObjective) => {
|
|
149
|
-
|
|
150
|
-
|
|
151
|
-
|
|
152
|
-
|
|
153
|
-
|
|
154
|
-
|
|
155
|
-
|
|
203
|
+
preparedCandidates.set(candidate, { operation, capture: (expectedHome, expectedScope, expectedObjective) => {
|
|
204
|
+
if (!retained)
|
|
205
|
+
refuse("request_candidate_not_retained");
|
|
206
|
+
if (home !== expectedHome || hybridDigest(scope) !== hybridDigest(expectedScope) || objective !== expectedObjective)
|
|
207
|
+
refuse("foreign_request_candidate");
|
|
208
|
+
verify();
|
|
209
|
+
return operation === "memory-upgrade" ? prepareMemoryUpgradeRequestInput(scope, selectionBytes, intakeBytes, objective)
|
|
210
|
+
: prepareMemoryInitRequestInput(scope, selectionBytes, intakeBytes, objective);
|
|
211
|
+
} });
|
|
156
212
|
return Object.freeze(candidate);
|
|
157
213
|
}
|
|
@@ -26,9 +26,9 @@ function text(bytes) {
|
|
|
26
26
|
}
|
|
27
27
|
/** Data-only decoding for SQL custody/recovery. No Home, files, engine registry,
|
|
28
28
|
* installed canon, current profiles or provider is consulted. */
|
|
29
|
-
|
|
29
|
+
function readMemoryRequestInput(value, scope, objective, operation) {
|
|
30
30
|
if (!exact(value, ["schema_id", "scope", "intake_base64", "intake_sha256", "selection_base64", "selection_sha256"])
|
|
31
|
-
|| !object(value) || value.schema_id !==
|
|
31
|
+
|| !object(value) || value.schema_id !== `dd-flow/${operation}-request-input@1`
|
|
32
32
|
|| hybridDigest(value.scope) !== hybridDigest(scope))
|
|
33
33
|
refuse("invalid_input_scope");
|
|
34
34
|
const intake = decode(value.intake_base64, value.intake_sha256, 64 * 1024);
|
|
@@ -44,8 +44,10 @@ export function readMemoryInitRequestInput(value, scope, objective) {
|
|
|
44
44
|
throw error;
|
|
45
45
|
return refuse("invalid_selection_json");
|
|
46
46
|
}
|
|
47
|
-
if (!exact(selection, ["schema_id", "scope", "execution", "git_policy", "objective", "engine_checksum", "receipts"
|
|
48
|
-
|
|
47
|
+
if (!exact(selection, ["schema_id", "scope", "execution", "git_policy", "objective", "engine_checksum", "receipts",
|
|
48
|
+
...(operation === "memory-upgrade" ? ["target_installation_sha256"] : [])])
|
|
49
|
+
|| !object(selection) || selection.schema_id !== `dd-flow/${operation}-request-selection@1`
|
|
50
|
+
|| operation === "memory-upgrade" && !digest(selection.target_installation_sha256)
|
|
49
51
|
|| hybridDigest(selection.scope) !== hybridDigest(scope) || selection.objective !== objective
|
|
50
52
|
|| !object(selection.execution) || !exact(selection.git_policy, ["route", "integrationBranch"])
|
|
51
53
|
|| !object(selection.git_policy) || !["integration_branch_direct", "feature_worktree"].includes(String(selection.git_policy.route))
|
|
@@ -55,12 +57,26 @@ export function readMemoryInitRequestInput(value, scope, objective) {
|
|
|
55
57
|
refuse("invalid_original_selection");
|
|
56
58
|
return { intake, selectionBytes, selection: selection };
|
|
57
59
|
}
|
|
58
|
-
|
|
60
|
+
function prepareMemoryRequestInput(scope, selection, intake, objective, operation) {
|
|
59
61
|
if (!selection.byteLength || selection.byteLength > 256 * 1024 || !intake.byteLength || intake.byteLength > 64 * 1024)
|
|
60
62
|
refuse("unbounded_original_input");
|
|
61
|
-
const value = { schema_id:
|
|
63
|
+
const value = { schema_id: `dd-flow/${operation}-request-input@1`, scope: structuredClone(scope),
|
|
62
64
|
intake_base64: Buffer.from(intake).toString("base64"), intake_sha256: sha(intake),
|
|
63
65
|
selection_base64: Buffer.from(selection).toString("base64"), selection_sha256: sha(selection) };
|
|
64
|
-
|
|
66
|
+
readMemoryRequestInput(value, scope, objective, operation);
|
|
65
67
|
return value;
|
|
66
68
|
}
|
|
69
|
+
export function readMemoryInitRequestInput(value, scope, objective) {
|
|
70
|
+
const read = readMemoryRequestInput(value, scope, objective, "memory-init");
|
|
71
|
+
return { ...read, selection: read.selection };
|
|
72
|
+
}
|
|
73
|
+
export function readMemoryUpgradeRequestInput(value, scope, objective) {
|
|
74
|
+
const read = readMemoryRequestInput(value, scope, objective, "memory-upgrade");
|
|
75
|
+
return { ...read, selection: read.selection };
|
|
76
|
+
}
|
|
77
|
+
export function prepareMemoryInitRequestInput(scope, selection, intake, objective) {
|
|
78
|
+
return prepareMemoryRequestInput(scope, selection, intake, objective, "memory-init");
|
|
79
|
+
}
|
|
80
|
+
export function prepareMemoryUpgradeRequestInput(scope, selection, intake, objective) {
|
|
81
|
+
return prepareMemoryRequestInput(scope, selection, intake, objective, "memory-upgrade");
|
|
82
|
+
}
|
|
@@ -9,6 +9,9 @@ import { readMemoryInitRunAllocation } from "./memory-init-run-allocation.js";
|
|
|
9
9
|
import { readMemoryInitProjectBootstrap } from "./memory-init-project-bootstrap.js";
|
|
10
10
|
import { bundledHandlerContext } from "./stage-handlers.js";
|
|
11
11
|
import { hybridDigest } from "./hybrid-contracts.js";
|
|
12
|
+
import { readMemoryUpgradeBootstrapReadiness } from "./memory-upgrade-bootstrap-readiness.js";
|
|
13
|
+
import { readUpgradeStorageReadiness } from "./memory-upgrade-storage-readiness.js";
|
|
14
|
+
import { nextDescriptorRunId } from "./descriptor-run-start.js";
|
|
12
15
|
const bytes = (value) => Buffer.from(JSON.stringify(value));
|
|
13
16
|
const hash = (value) => createHash("sha256").update(value).digest("hex");
|
|
14
17
|
function refuse(reason) {
|
|
@@ -29,6 +32,48 @@ export function prepareMemoryInitRunBundle(context, input) {
|
|
|
29
32
|
if (!ready || !allocation || !bootstrap || allocation.project_id !== ready.projectId
|
|
30
33
|
|| ready.authority.binding.execution_sha256 !== input.execution.sha256)
|
|
31
34
|
refuse("bootstrap_selection_changed");
|
|
35
|
+
input.source.revalidate();
|
|
36
|
+
const flow = input.source.resolveRegisteredFlow(), canon = input.source.canonProvenance();
|
|
37
|
+
const release = JSON.parse(Buffer.from(ready.authority.binding.installation_manifest_base64, "base64").toString("utf8"));
|
|
38
|
+
if (input.source.installationSha256() !== ready.authority.binding.installation_manifest_sha256
|
|
39
|
+
|| flow.sha256 !== snapshot.settings.descriptor_sha256 || snapshot.settings.flow_id !== "mb-init"
|
|
40
|
+
|| canon.version !== release.canon.version || canon.commit !== release.canon.commit
|
|
41
|
+
|| canon.archive_sha256 !== release.canon.archive.sha256
|
|
42
|
+
|| !input.engine.manifestBytes().equals(Buffer.from(ready.authority.binding.engine_manifest_base64, "base64")))
|
|
43
|
+
refuse("frozen_source_changed");
|
|
44
|
+
return prepareMemoryRunBundle(input, ready, { runId: allocation.run_id, boundAt: bootstrap.receipt.created_at,
|
|
45
|
+
flowId: "mb-init", projectPack: { sha256: ready.authority.binding.pack_sha256, source_commit: canon.commit } });
|
|
46
|
+
}
|
|
47
|
+
/** Upgrade executes the target canon but starts with the original project pack.
|
|
48
|
+
* It must not claim that publication has already happened at RUN allocation. */
|
|
49
|
+
export function prepareMemoryUpgradeRunBundle(context, input) {
|
|
50
|
+
if (!(input.source instanceof PreparedInstalledFlowSource) || !(input.execution instanceof FrozenGenericExecution)
|
|
51
|
+
|| !(input.engine instanceof AdmittedFrozenEngine))
|
|
52
|
+
refuse("unprepared_selection");
|
|
53
|
+
const snapshot = input.execution.snapshot(), scope = { requestId: snapshot.scope.requestId,
|
|
54
|
+
projectRoot: snapshot.scope.projectRoot, workspaceRoot: snapshot.scope.workspace };
|
|
55
|
+
const ready = readMemoryUpgradeBootstrapReadiness(context, scope), storage = readUpgradeStorageReadiness(context, scope);
|
|
56
|
+
if (!ready || !storage?.selection || !ready.hasFlowCapability || !ready.hasGitPolicy || !ready.hasLintCapability
|
|
57
|
+
|| !ready.hasPreservationBaseline || ready.authority.binding.execution_sha256 !== input.execution.sha256
|
|
58
|
+
|| ready.authority.binding.selection_sha256 !== storage.selection.sha256)
|
|
59
|
+
refuse("bootstrap_selection_changed");
|
|
60
|
+
const p = storage.selection.payload, canon = input.source.canonProvenance();
|
|
61
|
+
input.source.revalidate();
|
|
62
|
+
if (input.source.sha256 !== p.source.sha256 || input.source.installationSha256() !== p.release.manifest_sha256
|
|
63
|
+
|| snapshot.settings.flow_id !== "mb-upgrade" || input.source.resolveRegisteredFlow().sha256 !== snapshot.settings.descriptor_sha256
|
|
64
|
+
|| canon.version !== storage.selection.release.canon.version || canon.commit !== storage.selection.release.canon.commit
|
|
65
|
+
|| canon.archive_sha256 !== storage.selection.release.canon.archive.sha256
|
|
66
|
+
|| !input.engine.manifestBytes().equals(Buffer.from(ready.authority.binding.engine_manifest_base64, "base64")))
|
|
67
|
+
refuse("frozen_source_changed");
|
|
68
|
+
return prepareMemoryRunBundle(input, ready, {
|
|
69
|
+
runId: nextDescriptorRunId(context, { projectRoot: scope.projectRoot, requestId: scope.requestId, slug: "mb-upgrade" }),
|
|
70
|
+
boundAt: ready.createdAt, flowId: "mb-upgrade",
|
|
71
|
+
projectPack: { sha256: ready.authority.receipt.project_current.pack_sha256, source_commit: p.current.source_commit }
|
|
72
|
+
});
|
|
73
|
+
}
|
|
74
|
+
function prepareMemoryRunBundle(input, ready, selected) {
|
|
75
|
+
const snapshot = input.execution.snapshot(), scope = { requestId: snapshot.scope.requestId,
|
|
76
|
+
projectRoot: snapshot.scope.projectRoot, workspaceRoot: snapshot.scope.workspace };
|
|
32
77
|
const { bootstrapIdentity, receipts: bootstrapReceipts } = ready.admission;
|
|
33
78
|
if (!bootstrapIdentity || !bootstrapReceipts)
|
|
34
79
|
refuse("bootstrap_admission_missing");
|
|
@@ -43,22 +88,14 @@ export function prepareMemoryInitRunBundle(context, input) {
|
|
|
43
88
|
refuse("execution_receipt_collision");
|
|
44
89
|
receipts.set(reference.sha256, structuredClone(fact));
|
|
45
90
|
}
|
|
46
|
-
input.source.revalidate();
|
|
47
91
|
const flow = input.source.resolveRegisteredFlow(), canon = input.source.canonProvenance();
|
|
48
|
-
const
|
|
49
|
-
if (input.source.installationSha256() !== ready.authority.binding.installation_manifest_sha256
|
|
50
|
-
|| flow.sha256 !== snapshot.settings.descriptor_sha256 || snapshot.settings.flow_id !== "mb-init"
|
|
51
|
-
|| canon.version !== release.canon.version || canon.commit !== release.canon.commit
|
|
52
|
-
|| canon.archive_sha256 !== release.canon.archive.sha256
|
|
53
|
-
|| !input.engine.manifestBytes().equals(Buffer.from(ready.authority.binding.engine_manifest_base64, "base64")))
|
|
54
|
-
refuse("frozen_source_changed");
|
|
55
|
-
const manifest = input.engine.manifest(), runId = allocation.run_id;
|
|
92
|
+
const manifest = input.engine.manifest(), runId = selected.runId;
|
|
56
93
|
if (typeof manifest.integrity.checksum !== "string")
|
|
57
94
|
refuse("missing_engine_checksum");
|
|
58
95
|
// The accepted bootstrap timestamp makes reconstruction stable after a lost
|
|
59
96
|
// reply. Current time is not a new selector for this request's engine binding.
|
|
60
97
|
const binding = { schema_id: "dd-flow/run-engine-binding@1", run_id: runId, project_root: scope.projectRoot,
|
|
61
|
-
bound_at:
|
|
98
|
+
bound_at: selected.boundAt, source: "run_creation", reason: selected.flowId === "mb-init" ? "Request-bound memory init bootstrap" : "Request-bound memory upgrade bootstrap",
|
|
62
99
|
engine: { package_name: manifest.package_name, package_version: manifest.package_version, engine_version: manifest.engine_version,
|
|
63
100
|
integrity_checksum: manifest.integrity.checksum, snapshot_root: manifest.snapshot_root }, probe: { status: "not_required", command: null } };
|
|
64
101
|
const blobs = input.source.blobs();
|
|
@@ -84,8 +121,8 @@ export function prepareMemoryInitRunBundle(context, input) {
|
|
|
84
121
|
for (const evidence of ready.authority.evidence)
|
|
85
122
|
put(evidence.path, Buffer.from(evidence.base64, "base64"));
|
|
86
123
|
const receipt = put("receipts/bootstrap.json", bytes(ready.authority.receipt));
|
|
87
|
-
const provenance = { canon, project_pack:
|
|
88
|
-
const bundle = { schema_id: "dd-flow/execution-bundle@1", run_id: runId, flow_id:
|
|
124
|
+
const provenance = { canon, project_pack: selected.projectPack };
|
|
125
|
+
const bundle = { schema_id: "dd-flow/execution-bundle@1", run_id: runId, flow_id: selected.flowId,
|
|
89
126
|
descriptor: input.source.descriptorReference(), execution_settings: execution, profile_settings: profile,
|
|
90
127
|
engine_binding: engineBinding, engine_manifest: engineManifest, bootstrap_receipts: [receipt], ...provenance,
|
|
91
128
|
inventory: [...blobs].map(([logical, content]) => ({ path: logical, sha256: hash(content) })) };
|
|
@@ -18,7 +18,7 @@ const primaryKeys = {
|
|
|
18
18
|
};
|
|
19
19
|
function refuse(reason) { throw new AppError("memory_init_storage_incompatible", "Executing CLI cannot admit memory-init runtime storage without migration or repair", 1, { reason, effect: "no_effect" }); }
|
|
20
20
|
const normalize = (sql) => sql.replace(/\s+/g, " ").trim().replace(/;$/, "");
|
|
21
|
-
function inspect(context) {
|
|
21
|
+
function inspect(context, upgrade = false) {
|
|
22
22
|
if (!context.db.writable || !path.isAbsolute(context.ddFlowHome) || path.resolve(context.db.path) !== path.join(path.resolve(context.ddFlowHome), "db.sqlite"))
|
|
23
23
|
refuse("foreign_or_readonly_store");
|
|
24
24
|
const contract = context.db.all("SELECT id, version FROM dd_flow_writer_contract");
|
|
@@ -26,14 +26,15 @@ function inspect(context) {
|
|
|
26
26
|
|| context.db.get("SELECT dd_flow_writer_version() AS version")?.version !== STORE_WRITER_VERSION)
|
|
27
27
|
refuse("writer_contract_mismatch");
|
|
28
28
|
const schema = {};
|
|
29
|
-
|
|
29
|
+
const selectedFootprint = upgrade ? { ...footprint, runtime_config: ["key", "value_json", "created_at", "updated_at"] } : footprint;
|
|
30
|
+
for (const [table, required] of Object.entries(selectedFootprint)) {
|
|
30
31
|
const columns = context.db.all(`PRAGMA table_info("${table}")`);
|
|
31
32
|
if (required.some(name => !columns.some(column => column.name === name)))
|
|
32
33
|
refuse(`missing_operation_column:${table}`);
|
|
33
34
|
if (required.some(name => columns.find(column => column.name === name)?.type.toUpperCase() !== (table === "audit_events" && ["id", "forced"].includes(name) ? "INTEGER" : "TEXT")))
|
|
34
35
|
refuse(`operation_column_type_mismatch:${table}`);
|
|
35
36
|
const key = columns.filter(column => column.pk > 0).sort((a, b) => a.pk - b.pk).map(column => column.name);
|
|
36
|
-
if (JSON.stringify(key) !== JSON.stringify(primaryKeys[table]))
|
|
37
|
+
if (JSON.stringify(key) !== JSON.stringify(table === "runtime_config" ? ["key"] : primaryKeys[table]))
|
|
37
38
|
refuse(`operation_primary_key_mismatch:${table}`);
|
|
38
39
|
const fences = [];
|
|
39
40
|
for (const action of ["INSERT", "UPDATE", "DELETE"]) {
|
|
@@ -56,14 +57,23 @@ function inspect(context) {
|
|
|
56
57
|
export class PreparedMemoryInitRuntimeStorage {
|
|
57
58
|
sha256;
|
|
58
59
|
#facts;
|
|
59
|
-
|
|
60
|
+
#upgrade;
|
|
61
|
+
constructor(facts, upgrade) { this.#facts = facts; this.#upgrade = upgrade; this.sha256 = hybridDigest(facts); Object.freeze(this); }
|
|
60
62
|
static prepare(context) {
|
|
63
|
+
return this.prepareFor(context, false);
|
|
64
|
+
}
|
|
65
|
+
/** Upgrade reuses the RUN/Work footprint and additionally needs retained
|
|
66
|
+
* request custody. This remains a non-migrating connection observation. */
|
|
67
|
+
static prepareUpgrade(context) {
|
|
68
|
+
return this.prepareFor(context, true);
|
|
69
|
+
}
|
|
70
|
+
static prepareFor(context, upgrade) {
|
|
61
71
|
try {
|
|
62
72
|
return context.db.readSnapshot(() => {
|
|
63
|
-
const facts = inspect(context), integrity = context.db.all("PRAGMA quick_check");
|
|
73
|
+
const facts = inspect(context, upgrade), integrity = context.db.all("PRAGMA quick_check");
|
|
64
74
|
if (integrity.length !== 1 || integrity[0]?.quick_check !== "ok" || context.db.all("PRAGMA foreign_key_check").length)
|
|
65
75
|
refuse("storage_integrity_failed");
|
|
66
|
-
return new PreparedMemoryInitRuntimeStorage(facts);
|
|
76
|
+
return new PreparedMemoryInitRuntimeStorage(facts, upgrade);
|
|
67
77
|
});
|
|
68
78
|
}
|
|
69
79
|
catch (error) {
|
|
@@ -75,7 +85,7 @@ export class PreparedMemoryInitRuntimeStorage {
|
|
|
75
85
|
facts() { return { ...this.#facts }; }
|
|
76
86
|
/** Writer-safe metadata fence; expensive integrity scan remains preparation. */
|
|
77
87
|
assertCompatibility(context) {
|
|
78
|
-
if (hybridDigest(inspect(context)) !== this.sha256)
|
|
88
|
+
if (hybridDigest(inspect(context, this.#upgrade)) !== this.sha256)
|
|
79
89
|
refuse("storage_changed_after_preparation");
|
|
80
90
|
}
|
|
81
91
|
}
|