@deksden-com/dd-flow-cli 0.9.0-beta.143 → 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 +65 -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 +74 -9
- 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
|
@@ -0,0 +1,430 @@
|
|
|
1
|
+
import fs from "node:fs";
|
|
2
|
+
import path from "node:path";
|
|
3
|
+
import { createHash } from "node:crypto";
|
|
4
|
+
import { AppError, errorRecord } from "../shared/errors.js";
|
|
5
|
+
import { assertImportAdmission } from "../storage/import-guard.js";
|
|
6
|
+
import { publishImmutableInput } from "../storage/immutable-input.js";
|
|
7
|
+
import { captureMemoryUpgradeRequestCandidate, readMemoryUpgradeRequestCandidate } from "./memory-init-request-candidate.js";
|
|
8
|
+
import { readMemoryUpgradeRequestInput } from "./memory-init-request-input.js";
|
|
9
|
+
import { PreparedInstalledFlowSource } from "./installed-flow-source.js";
|
|
10
|
+
import { PreparedMemoryLayer } from "./memory-layer.js";
|
|
11
|
+
import { FrozenGenericExecution } from "./generic-execution.js";
|
|
12
|
+
import { ResolvedFlowDescriptor } from "./flow-descriptors.js";
|
|
13
|
+
import { readOperatorProfiles } from "./operator-profiles.js";
|
|
14
|
+
import { AdmittedFrozenEngine } from "./frozen-engine.js";
|
|
15
|
+
import { prepareMemoryInitEngineRelease, revalidateMemoryInitEngineRelease, selectMemoryInitInstalledEngine } from "./memory-init-engine-release.js";
|
|
16
|
+
import { prepareMemoryUpgradeInventory, captureUpgradePreservationBaseline, readUpgradePreservationBaseline, captureUpgradeProjectLinks } from "./memory-upgrade-inventory.js";
|
|
17
|
+
import { getProjectVersionStatus } from "./version-status.js";
|
|
18
|
+
import { observeDescriptorWorkspace } from "./descriptor-run-start.js";
|
|
19
|
+
import { readBoundedReleaseFile } from "./release-source.js";
|
|
20
|
+
import { hybridDigest } from "./hybrid-contracts.js";
|
|
21
|
+
import { adjacentMigrationChain, readImpacts } from "./migrations.js";
|
|
22
|
+
import { readSetupStatus } from "./setup.js";
|
|
23
|
+
import { inspectMemoryGitExecutable } from "./memory-init-preflight.js";
|
|
24
|
+
import { parseDocument } from "yaml";
|
|
25
|
+
import semver from "semver";
|
|
26
|
+
import { observeMemoryGitPolicySelection } from "./memory-init-git-policy.js";
|
|
27
|
+
import { captureUpgradeProjectFiles, readUpgradeProjectFiles } from "./memory-upgrade-inventory.js";
|
|
28
|
+
// ponytail: one bounded immutable record, like startup@3; split blobs only if
|
|
29
|
+
// real canonical packs outgrow the existing 64 MiB startup-input ceiling.
|
|
30
|
+
const limit = 64 * 1024 * 1024;
|
|
31
|
+
const hash = (bytes) => createHash("sha256").update(bytes).digest("hex");
|
|
32
|
+
function refuse(reason) {
|
|
33
|
+
throw new AppError("memory_upgrade_selection_invalid", "Upgrade requires its original resolved selection", 1, { reason, effect: "no_effect", next_action: "reconcile_exact_request" });
|
|
34
|
+
}
|
|
35
|
+
function exists(file) {
|
|
36
|
+
try {
|
|
37
|
+
fs.lstatSync(file);
|
|
38
|
+
return true;
|
|
39
|
+
}
|
|
40
|
+
catch (error) {
|
|
41
|
+
if (error.code === "ENOENT")
|
|
42
|
+
return false;
|
|
43
|
+
throw error;
|
|
44
|
+
}
|
|
45
|
+
}
|
|
46
|
+
/** Data admission only. These fixed declaration names do not register code.
|
|
47
|
+
* RUN/bootstrap must still admit the actual selected engine's handler registry. */
|
|
48
|
+
function descriptorData(source) {
|
|
49
|
+
const reference = source.descriptorReference();
|
|
50
|
+
if (reference.path !== "mb-upgrade/flow-descriptor.json")
|
|
51
|
+
refuse("foreign_flow");
|
|
52
|
+
const blobs = source.blobs(), flow = ResolvedFlowDescriptor.admit(blobs.get(reference.path), reference.sha256, {
|
|
53
|
+
blobs, entryHandlers: new Set(["memory-upgrade-stage-entry"]), finishHandlers: new Set(["memory-upgrade-stage-finish"])
|
|
54
|
+
});
|
|
55
|
+
const stages = ["preflight", "diff-analysis", "upgrade", "verify", "review", "finish"];
|
|
56
|
+
if (flow.descriptor.id !== "mb-upgrade" || flow.descriptor.entry_stage !== "preflight"
|
|
57
|
+
|| flow.descriptor.stages.length !== stages.length || flow.descriptor.stages.some((stage, i) => stage.id !== stages[i]))
|
|
58
|
+
refuse("foreign_flow_layout");
|
|
59
|
+
return flow;
|
|
60
|
+
}
|
|
61
|
+
function files(input, baseline = false) {
|
|
62
|
+
if (!input || !Array.isArray(input.files) || input.files.length > 20_000)
|
|
63
|
+
refuse("invalid_layer");
|
|
64
|
+
const entries = new Map();
|
|
65
|
+
for (const entry of input.files) {
|
|
66
|
+
if (!entry || Object.keys(entry).sort().join() !== "base64,path" || typeof entry.base64 !== "string"
|
|
67
|
+
|| entry.base64.length > 2 * 1024 * 1024 || entries.has(entry.path))
|
|
68
|
+
refuse("invalid_layer");
|
|
69
|
+
const bytes = Buffer.from(entry.base64, "base64");
|
|
70
|
+
if (bytes.toString("base64") !== entry.base64)
|
|
71
|
+
refuse("invalid_layer");
|
|
72
|
+
entries.set(entry.path, bytes);
|
|
73
|
+
}
|
|
74
|
+
return PreparedMemoryLayer.restoreRetained(entries, input.sha256, input.installation_sha256, baseline);
|
|
75
|
+
}
|
|
76
|
+
function current(home, scope, layer, env, baselineInstallationSha256, gitExecutable, includeProjectLinks = true, includeProjectFiles = true) {
|
|
77
|
+
const workspace = observeDescriptorWorkspace({ env }, scope.projectRoot, scope.workspaceRoot, gitExecutable);
|
|
78
|
+
let inventory = prepareMemoryUpgradeInventory(scope.workspaceRoot, layer);
|
|
79
|
+
const status = getProjectVersionStatus({ projectRoot: scope.workspaceRoot, rootSource: "explicit", canon: null });
|
|
80
|
+
const missingMarker = status?.memory_bank.status === "unknown" && status.memory_bank.reason === "memory_bank_version_missing"
|
|
81
|
+
|| status?.memory_bank.source === ".memory-bank/mbb/index.md";
|
|
82
|
+
const canonVersion = missingMarker ? status?.flow_pack.canon_version_at_source_commit : status?.memory_bank.version;
|
|
83
|
+
const packVersion = status?.flow_pack.canon_version_at_source_commit;
|
|
84
|
+
if (!status || status.memory_bank.status !== "present" && !missingMarker || status.flow_pack.status !== "present" || !canonVersion
|
|
85
|
+
|| !packVersion || semver.valid(canonVersion) !== canonVersion || semver.valid(packVersion) !== packVersion
|
|
86
|
+
|| semver.gt(canonVersion, packVersion) || !/^[a-f0-9]{40}$/.test(status.flow_pack.source_commit ?? ""))
|
|
87
|
+
refuse("invalid_current_provenance");
|
|
88
|
+
const versionPath = missingMarker ? "index.md" : status.memory_bank.source?.replace(/^\.memory-bank\//, "");
|
|
89
|
+
if (typeof versionPath !== "string" || !inventory.inventory.entries.some(entry => entry.path === versionPath && entry.current?.kind === "file"))
|
|
90
|
+
refuse("unowned_version_source");
|
|
91
|
+
const manifest = readBoundedReleaseFile(inventory.inventory.current_manifest.path, 1024 * 1024);
|
|
92
|
+
if (hash(manifest) !== inventory.inventory.current_manifest.sha256)
|
|
93
|
+
refuse("current_changed");
|
|
94
|
+
const declared = JSON.parse(manifest.toString("utf8"));
|
|
95
|
+
if (declared.canon_version_at_source_commit !== packVersion || declared.source_commit !== status.flow_pack.source_commit)
|
|
96
|
+
refuse("current_changed");
|
|
97
|
+
const baseline = baselineInstallationSha256 === null ? null : PreparedMemoryLayer.fromCommittedCanon(home, declared, baselineInstallationSha256);
|
|
98
|
+
if (missingMarker) {
|
|
99
|
+
// Older real init outputs omitted this project marker. Their independently
|
|
100
|
+
// installed old canon, not mutable manifest text alone, supplies provenance.
|
|
101
|
+
const index = readBoundedReleaseFile(path.join(scope.workspaceRoot, ".memory-bank", versionPath), 1024 * 1024).toString("utf8");
|
|
102
|
+
const frontmatter = index.match(/^---\n([\s\S]*?)\n---(?:\n|$)/);
|
|
103
|
+
const document = frontmatter ? parseDocument(frontmatter[1]) : null;
|
|
104
|
+
if (!baseline || !document || document.errors.length || document.has("memory_bank_version"))
|
|
105
|
+
refuse("missing_version_marker_requires_canonical_baseline");
|
|
106
|
+
if (status.memory_bank.version && status.memory_bank.version !== canonVersion) {
|
|
107
|
+
// Canonical guidance historically carried stale prose version labels.
|
|
108
|
+
// Ignore that fallback only when it is the exact authenticated old guide;
|
|
109
|
+
// a divergent local version claim is an explicit conflict, not legacy.
|
|
110
|
+
const guide = inventory.inventory.entries.find(entry => entry.path === "mbb/index.md")?.current;
|
|
111
|
+
const original = baseline.files().get("mbb/index.md");
|
|
112
|
+
if (!original || guide?.kind !== "file" || guide.sha256 !== hash(original))
|
|
113
|
+
refuse("legacy_guidance_version_diverged");
|
|
114
|
+
}
|
|
115
|
+
}
|
|
116
|
+
if (baseline)
|
|
117
|
+
inventory = prepareMemoryUpgradeInventory(scope.workspaceRoot, layer, baseline);
|
|
118
|
+
const projectLinks = includeProjectLinks ? captureUpgradeProjectLinks(scope.workspaceRoot, gitExecutable ?? "git", env) : [];
|
|
119
|
+
return { workspace, current: { canon_version: canonVersion, source_commit: status.flow_pack.source_commit,
|
|
120
|
+
...(includeProjectFiles ? { project_files: captureUpgradeProjectFiles(scope.workspaceRoot, gitExecutable ?? "git", env) } : {}),
|
|
121
|
+
...(projectLinks.length ? { project_links: projectLinks } : {}),
|
|
122
|
+
...(canonVersion !== packVersion ? { flow_pack_canon_version: packVersion } : {}),
|
|
123
|
+
manifest_base64: manifest.toString("base64"), version_source: versionPath, inventory, old_tree: captureUpgradePreservationBaseline(inventory),
|
|
124
|
+
...(baseline ? { baseline_layer: { sha256: baseline.sha256, installation_sha256: baseline.installationSha256,
|
|
125
|
+
files: [...baseline.files()].map(([logical, bytes]) => ({ path: logical, base64: bytes.toString("base64") })) } } : {}) } };
|
|
126
|
+
}
|
|
127
|
+
function paths(candidate) {
|
|
128
|
+
const root = path.dirname(candidate.retain().file);
|
|
129
|
+
return { payload: path.join(root, "resolved-selection.json"), receipt: path.join(root, "resolved-selection.receipt.json") };
|
|
130
|
+
}
|
|
131
|
+
function impacts(layer, sourceVersion, targetVersion) {
|
|
132
|
+
const manifest = JSON.parse(layer.files().get("dd-flow/manifest.json").toString("utf8"));
|
|
133
|
+
const entries = readImpacts(path.join(manifest.canon_memory_bank_root, "release-impact"));
|
|
134
|
+
return { chain: adjacentMigrationChain(sourceVersion, targetVersion, entries),
|
|
135
|
+
files: entries.map(entry => {
|
|
136
|
+
const reference = entry.source_reference;
|
|
137
|
+
const content = readBoundedReleaseFile(reference.path, 1024 * 1024);
|
|
138
|
+
if (hash(content) !== reference.sha256)
|
|
139
|
+
refuse("impact_changed");
|
|
140
|
+
return { ...reference, base64: content.toString("base64") };
|
|
141
|
+
}) };
|
|
142
|
+
}
|
|
143
|
+
function assertMixedCurrentContext(current, impact) {
|
|
144
|
+
const pack = current.flow_pack_canon_version;
|
|
145
|
+
if (pack === undefined)
|
|
146
|
+
return;
|
|
147
|
+
// An independently updated process pack is a real intermediate node, not
|
|
148
|
+
// permission to invent a migration hop or to claim the MB already upgraded.
|
|
149
|
+
if (semver.valid(current.canon_version) !== current.canon_version || semver.valid(pack) !== pack
|
|
150
|
+
|| !semver.lt(current.canon_version, pack) || impact.chain.status !== "ok"
|
|
151
|
+
|| !impact.chain.steps.some(step => step.to === pack))
|
|
152
|
+
refuse("invalid_mixed_current_context");
|
|
153
|
+
}
|
|
154
|
+
/** Historical input custody only. No current project/cache/profile or engine
|
|
155
|
+
* code is inspected; actual input restoration is a separate operation below. */
|
|
156
|
+
export function readMemoryUpgradeSelection(home, scope) {
|
|
157
|
+
const original = readMemoryUpgradeRequestCandidate(home, scope);
|
|
158
|
+
if (!original)
|
|
159
|
+
return null;
|
|
160
|
+
const locations = paths(original.candidate), hasPayload = exists(locations.payload), hasReceipt = exists(locations.receipt);
|
|
161
|
+
if (!hasPayload && !hasReceipt)
|
|
162
|
+
return null;
|
|
163
|
+
if (!hasPayload || !hasReceipt)
|
|
164
|
+
refuse("selection_custody_missing");
|
|
165
|
+
try {
|
|
166
|
+
const bytes = readBoundedReleaseFile(locations.payload, limit), sha256 = hash(bytes);
|
|
167
|
+
const receipt = JSON.parse(readBoundedReleaseFile(locations.receipt, 16 * 1024).toString("utf8"));
|
|
168
|
+
const requestSha256 = hybridDigest(original.input);
|
|
169
|
+
if (hybridDigest(receipt) !== hybridDigest({ schema_id: "dd-flow/memory-upgrade-selection-receipt@1", scope,
|
|
170
|
+
request_input_sha256: requestSha256, selection_sha256: sha256 }))
|
|
171
|
+
refuse("selection_bytes_changed");
|
|
172
|
+
return { ...decodeMemoryUpgradeSelection(bytes, sha256, original.input, scope), original, bytes };
|
|
173
|
+
}
|
|
174
|
+
catch (error) {
|
|
175
|
+
if (error instanceof AppError)
|
|
176
|
+
throw error;
|
|
177
|
+
return refuse("invalid_retained_selection");
|
|
178
|
+
}
|
|
179
|
+
}
|
|
180
|
+
/** SQL-safe data decoding for accepted custody/recovery. No filesystem, current
|
|
181
|
+
* defaults, engine registry, storage probe or provider is consulted here. */
|
|
182
|
+
export function decodeMemoryUpgradeSelection(bytes, expectedSha256, input, scope) {
|
|
183
|
+
try {
|
|
184
|
+
if (bytes.byteLength > limit || hash(bytes) !== expectedSha256)
|
|
185
|
+
refuse("selection_bytes_changed");
|
|
186
|
+
const objective = new TextDecoder("utf-8", { fatal: true, ignoreBOM: true }).decode(Buffer.from(input.intake_base64, "base64"));
|
|
187
|
+
const raw = readMemoryUpgradeRequestInput(input, scope, objective).selection;
|
|
188
|
+
const payload = JSON.parse(new TextDecoder("utf-8", { fatal: true }).decode(bytes));
|
|
189
|
+
if (!payload || Object.keys(payload).filter(key => key !== "git").sort().join() !== "current,engine,execution,impact,layer,release,request_input_sha256,schema_id,scope,source,workspace"
|
|
190
|
+
|| payload.schema_id !== "dd-flow/memory-upgrade-selection@1" || hybridDigest(payload.scope) !== hybridDigest(scope)
|
|
191
|
+
|| payload.request_input_sha256 !== hybridDigest(input))
|
|
192
|
+
refuse("foreign_selection");
|
|
193
|
+
if (payload.git !== undefined) {
|
|
194
|
+
const { executable, probe, policy } = payload.git;
|
|
195
|
+
if (Object.keys(payload.git).sort().join() !== "executable,policy,probe"
|
|
196
|
+
|| typeof executable !== "string" || !path.isAbsolute(executable)
|
|
197
|
+
|| !probe || Object.keys(probe).sort().join() !== "executable_sha256,stderr_sha256,stdout_sha256,version"
|
|
198
|
+
|| typeof probe.version !== "string" || !probe.version
|
|
199
|
+
|| [probe.executable_sha256, probe.stdout_sha256, probe.stderr_sha256].some(value => typeof value !== "string" || !/^[a-f0-9]{64}$/.test(value))
|
|
200
|
+
|| !policy || Object.keys(policy).sort().join() !== "initial_head,integration_branch,push_authorized,repository,route,source,workspace_branch"
|
|
201
|
+
|| typeof policy.repository !== "string" || !path.isAbsolute(policy.repository)
|
|
202
|
+
|| policy.route !== raw.git_policy.route || policy.integration_branch !== raw.git_policy.integrationBranch
|
|
203
|
+
|| policy.source !== "explicit_request" || policy.push_authorized !== false
|
|
204
|
+
|| policy.workspace_branch !== payload.workspace.branch || policy.initial_head !== payload.workspace.head)
|
|
205
|
+
refuse("invalid_git_selection");
|
|
206
|
+
}
|
|
207
|
+
if (!payload.release || typeof payload.release.manifest_base64 !== "string" || payload.release.manifest_base64.length > 2 * 1024 * 1024)
|
|
208
|
+
refuse("invalid_release_bytes");
|
|
209
|
+
const releaseBytes = Buffer.from(payload.release.manifest_base64, "base64");
|
|
210
|
+
if (releaseBytes.toString("base64") !== payload.release.manifest_base64 || hash(releaseBytes) !== raw.target_installation_sha256
|
|
211
|
+
|| payload.release.manifest_sha256 !== raw.target_installation_sha256)
|
|
212
|
+
refuse("release_bytes_changed");
|
|
213
|
+
const release = JSON.parse(new TextDecoder("utf-8", { fatal: true }).decode(releaseBytes));
|
|
214
|
+
if (payload.current?.old_tree !== undefined)
|
|
215
|
+
readUpgradePreservationBaseline(payload.current.old_tree, payload.current.inventory);
|
|
216
|
+
if (payload.current?.project_files !== undefined)
|
|
217
|
+
readUpgradeProjectFiles(payload.current.project_files);
|
|
218
|
+
if (payload.current?.project_links !== undefined) {
|
|
219
|
+
const links = payload.current.project_links, seen = new Set();
|
|
220
|
+
if (!Array.isArray(links) || links.length > 20_000 || JSON.stringify(links).length > 2 * 1024 * 1024)
|
|
221
|
+
refuse("invalid_project_links");
|
|
222
|
+
for (const link of links) {
|
|
223
|
+
if (!link || Object.keys(link).sort().join() !== "mode,path,sha256,target_base64"
|
|
224
|
+
|| typeof link.path !== "string" || path.isAbsolute(link.path)
|
|
225
|
+
|| link.path.split("/").some(part => !part || part === "." || part === "..")
|
|
226
|
+
|| seen.has(link.path) || typeof link.target_base64 !== "string"
|
|
227
|
+
|| Buffer.from(link.target_base64, "base64").toString("base64") !== link.target_base64
|
|
228
|
+
|| hash(Buffer.from(link.target_base64, "base64")) !== link.sha256
|
|
229
|
+
|| !Number.isInteger(link.mode) || link.mode < 0 || link.mode > 0o777)
|
|
230
|
+
refuse("invalid_project_links");
|
|
231
|
+
seen.add(link.path);
|
|
232
|
+
}
|
|
233
|
+
}
|
|
234
|
+
const baseline = payload.current?.baseline_layer ? files(payload.current.baseline_layer, true) : null;
|
|
235
|
+
assertMixedCurrentContext(payload.current, payload.impact);
|
|
236
|
+
if ((baseline?.sha256 ?? null) !== payload.current.inventory.inventory.baseline_layer_sha256)
|
|
237
|
+
refuse("changed_canonical_baseline");
|
|
238
|
+
if (baseline) {
|
|
239
|
+
const manifest = JSON.parse(baseline.files().get("dd-flow/manifest.json").toString("utf8"));
|
|
240
|
+
if (manifest.source_commit !== payload.current.source_commit || manifest.canon_version_at_source_commit !== (payload.current.flow_pack_canon_version ?? payload.current.canon_version))
|
|
241
|
+
refuse("foreign_canonical_baseline");
|
|
242
|
+
}
|
|
243
|
+
if (payload.current.version_source !== undefined && !payload.current.inventory.inventory.entries.some(entry => entry.path === payload.current.version_source && entry.current?.kind === "file"))
|
|
244
|
+
refuse("unowned_version_source");
|
|
245
|
+
return { payload, sha256: expectedSha256, release };
|
|
246
|
+
}
|
|
247
|
+
catch (error) {
|
|
248
|
+
if (error instanceof AppError)
|
|
249
|
+
throw error;
|
|
250
|
+
return refuse("invalid_retained_selection");
|
|
251
|
+
}
|
|
252
|
+
}
|
|
253
|
+
/** Restore prepared data and the exact immutable code, not storage readiness
|
|
254
|
+
* or productive handler admission. Never consults current operator selections. */
|
|
255
|
+
export function restoreMemoryUpgradeSelection(home, scope) {
|
|
256
|
+
const retained = readMemoryUpgradeSelection(home, scope);
|
|
257
|
+
if (!retained)
|
|
258
|
+
refuse("selection_required");
|
|
259
|
+
return { ...restoreMemoryUpgradeSelectionData(retained.bytes, retained.sha256, retained.original.input, scope), original: retained.original };
|
|
260
|
+
}
|
|
261
|
+
/** Restore the same owners from independently retained SQL bytes after the
|
|
262
|
+
* pre-storage candidate has been retired. This still admits the exact code;
|
|
263
|
+
* readable historical metadata alone is not engine execution authority. */
|
|
264
|
+
export function restoreMemoryUpgradeSelectionData(bytes, expectedSha256, input, scope) {
|
|
265
|
+
const { payload, sha256, release } = decodeMemoryUpgradeSelection(bytes, expectedSha256, input, scope);
|
|
266
|
+
try {
|
|
267
|
+
const objective = new TextDecoder("utf-8", { fatal: true, ignoreBOM: true }).decode(Buffer.from(input.intake_base64, "base64"));
|
|
268
|
+
const raw = readMemoryUpgradeRequestInput(input, scope, objective).selection;
|
|
269
|
+
const source = PreparedInstalledFlowSource.restoreRetainedData(payload.source.snapshot, payload.source.sha256), layer = files(payload.layer);
|
|
270
|
+
if (source.installationSha256() !== raw.target_installation_sha256 || layer.installationSha256 !== raw.target_installation_sha256)
|
|
271
|
+
refuse("foreign_target");
|
|
272
|
+
const receipts = new Map(payload.execution.receipts), receiptBytes = new Map();
|
|
273
|
+
if (receipts.size !== payload.execution.receipts.length || receipts.size > 2)
|
|
274
|
+
refuse("invalid_execution_receipts");
|
|
275
|
+
for (const [checksum, encoded] of payload.execution.receipt_blobs ?? []) {
|
|
276
|
+
const content = Buffer.from(encoded, "base64");
|
|
277
|
+
if (content.length > 256 * 1024 || content.toString("base64") !== encoded || hash(content) !== checksum || receiptBytes.has(checksum))
|
|
278
|
+
refuse("invalid_execution_receipt_bytes");
|
|
279
|
+
receiptBytes.set(checksum, content);
|
|
280
|
+
}
|
|
281
|
+
const execution = FrozenGenericExecution.restore(payload.execution.snapshot, payload.execution.sha256, descriptorData(source), { requestId: scope.requestId, projectRoot: scope.projectRoot, workspace: scope.workspaceRoot }, receipts);
|
|
282
|
+
const consumed = [execution.settings.attached_session_receipt, execution.settings.delegation.native_capacity_receipt].filter(ref => ref !== null);
|
|
283
|
+
if (receipts.size !== consumed.length || receiptBytes.size !== consumed.length || consumed.some(ref => !receiptBytes.has(ref.sha256)))
|
|
284
|
+
refuse("execution_receipt_bytes_missing");
|
|
285
|
+
const engine = AdmittedFrozenEngine.admitSelection(payload.engine.identity, payload.engine.manifest_sha256);
|
|
286
|
+
if (payload.engine.release_pairing.facts.installation_sha256 !== raw.target_installation_sha256
|
|
287
|
+
|| raw.engine_checksum !== null && raw.engine_checksum !== payload.engine.identity.integrity_checksum)
|
|
288
|
+
refuse("foreign_engine");
|
|
289
|
+
revalidateMemoryInitEngineRelease(payload.engine.release_pairing.facts, payload.engine.release_pairing.sha256, engine);
|
|
290
|
+
const canon = source.canonProvenance(), engineManifest = engine.manifest();
|
|
291
|
+
const layerManifest = JSON.parse(layer.files().get("dd-flow/manifest.json").toString("utf8"));
|
|
292
|
+
if (release.canon.version !== canon.version || release.canon.commit !== canon.commit || release.canon.archive.sha256 !== canon.archive_sha256
|
|
293
|
+
|| release.engine.package !== engineManifest.package_name || release.engine.version !== engineManifest.package_version
|
|
294
|
+
|| release.engine.archive.sha256 !== payload.engine.release_pairing.facts.archive_sha256
|
|
295
|
+
|| layerManifest.source_commit !== canon.commit || layerManifest.canon_version_at_source_commit !== canon.version)
|
|
296
|
+
refuse("foreign_release_context");
|
|
297
|
+
const impactEntries = payload.impact.files.map(reference => {
|
|
298
|
+
const bytes = Buffer.from(reference.base64, "base64");
|
|
299
|
+
if (bytes.length > 1024 * 1024 || bytes.toString("base64") !== reference.base64 || hash(bytes) !== reference.sha256)
|
|
300
|
+
refuse("impact_bytes_changed");
|
|
301
|
+
return { ...JSON.parse(bytes.toString("utf8")), source_reference: { path: reference.path, sha256: reference.sha256 } };
|
|
302
|
+
});
|
|
303
|
+
if (hybridDigest(adjacentMigrationChain(payload.current.canon_version, source.canonProvenance().version, impactEntries)) !== hybridDigest(payload.impact.chain))
|
|
304
|
+
refuse("changed_impact_chain");
|
|
305
|
+
if (payload.current.inventory.sha256 !== hybridDigest(payload.current.inventory.inventory)
|
|
306
|
+
|| payload.current.inventory.inventory.target_layer_sha256 !== layer.sha256
|
|
307
|
+
|| payload.current.inventory.inventory.workspace_root !== scope.workspaceRoot
|
|
308
|
+
|| hash(Buffer.from(payload.current.manifest_base64, "base64")) !== payload.current.inventory.inventory.current_manifest.sha256)
|
|
309
|
+
refuse("foreign_current_context");
|
|
310
|
+
const oldManifest = JSON.parse(Buffer.from(payload.current.manifest_base64, "base64").toString("utf8"));
|
|
311
|
+
if (oldManifest.source_commit !== payload.current.source_commit || oldManifest.canon_version_at_source_commit !== (payload.current.flow_pack_canon_version ?? payload.current.canon_version))
|
|
312
|
+
refuse("foreign_current_context");
|
|
313
|
+
const oldTree = payload.current.old_tree ? readUpgradePreservationBaseline(payload.current.old_tree, payload.current.inventory) : null;
|
|
314
|
+
const baselineLayer = payload.current.baseline_layer ? files(payload.current.baseline_layer, true) : null;
|
|
315
|
+
return { payload, sha256, source, layer, execution, engine, receipts, receiptBytes, release, oldTree, baselineLayer, bytes: Buffer.from(bytes) };
|
|
316
|
+
}
|
|
317
|
+
catch (error) {
|
|
318
|
+
if (error instanceof AppError)
|
|
319
|
+
throw error;
|
|
320
|
+
return refuse("invalid_retained_selection");
|
|
321
|
+
}
|
|
322
|
+
}
|
|
323
|
+
/** Pre-storage selection only: no SQLite open, project sync, RUN or provider.
|
|
324
|
+
* The receipt distinguishes never-selected from lost/partial selection, so a
|
|
325
|
+
* retry cannot silently replace its resolved profiles or selected release. */
|
|
326
|
+
export class PreparedMemoryUpgradeSelection {
|
|
327
|
+
sha256;
|
|
328
|
+
#bytes;
|
|
329
|
+
#payload;
|
|
330
|
+
#home;
|
|
331
|
+
#candidate;
|
|
332
|
+
#source;
|
|
333
|
+
#layer;
|
|
334
|
+
#engine;
|
|
335
|
+
#env;
|
|
336
|
+
constructor(home, candidate, payload, source, layer, engine, env) {
|
|
337
|
+
this.#home = home;
|
|
338
|
+
this.#candidate = candidate;
|
|
339
|
+
this.#payload = structuredClone(payload);
|
|
340
|
+
this.#bytes = Buffer.from(JSON.stringify(payload));
|
|
341
|
+
if (this.#bytes.length > limit)
|
|
342
|
+
refuse("unbounded_selection");
|
|
343
|
+
this.sha256 = hash(this.#bytes);
|
|
344
|
+
this.#source = source;
|
|
345
|
+
this.#layer = layer;
|
|
346
|
+
this.#engine = engine;
|
|
347
|
+
this.#env = { ...env };
|
|
348
|
+
Object.freeze(this);
|
|
349
|
+
}
|
|
350
|
+
static prepare(home, scope, candidate, options) {
|
|
351
|
+
const original = captureMemoryUpgradeRequestCandidate(candidate, home, scope, candidate.objective);
|
|
352
|
+
const locations = paths(candidate);
|
|
353
|
+
if (exists(locations.payload) || exists(locations.receipt))
|
|
354
|
+
refuse("restore_existing_selection");
|
|
355
|
+
assertImportAdmission(home);
|
|
356
|
+
const raw = readMemoryUpgradeRequestInput(original, scope, candidate.objective).selection;
|
|
357
|
+
const source = PreparedInstalledFlowSource.fromInstalledRelease(home, raw.target_installation_sha256, "mb-upgrade");
|
|
358
|
+
const layer = PreparedMemoryLayer.fromInstalledRelease(home, raw.target_installation_sha256, options.now);
|
|
359
|
+
const engine = selectMemoryInitInstalledEngine(home, raw.target_installation_sha256, raw.engine_checksum ?? undefined);
|
|
360
|
+
const receipts = new Map(options.receipts ?? []);
|
|
361
|
+
const ordered = (entries) => [...entries].sort(([a], [b]) => a.localeCompare(b));
|
|
362
|
+
if (hybridDigest(ordered([...receipts])) !== hybridDigest(ordered(raw.receipts)))
|
|
363
|
+
refuse("execution_receipt_authority_missing");
|
|
364
|
+
const execution = FrozenGenericExecution.prepare(descriptorData(source), readOperatorProfiles(home), raw.execution, { requestId: scope.requestId, projectRoot: scope.projectRoot, workspace: scope.workspaceRoot }, receipts);
|
|
365
|
+
const consumed = [execution.settings.attached_session_receipt, execution.settings.delegation.native_capacity_receipt].filter(ref => ref !== null);
|
|
366
|
+
const selectedReceipts = [];
|
|
367
|
+
const receiptBlobs = [];
|
|
368
|
+
for (const reference of consumed) {
|
|
369
|
+
const content = options.receiptBytes?.get(reference.sha256);
|
|
370
|
+
if (!content || content.byteLength > 256 * 1024 || hash(content) !== reference.sha256)
|
|
371
|
+
refuse("execution_receipt_bytes_missing");
|
|
372
|
+
selectedReceipts.push([reference.sha256, structuredClone(receipts.get(reference.sha256))]);
|
|
373
|
+
receiptBlobs.push([reference.sha256, Buffer.from(content).toString("base64")]);
|
|
374
|
+
}
|
|
375
|
+
const pairing = prepareMemoryInitEngineRelease(home, raw.target_installation_sha256, engine), manifest = engine.manifest();
|
|
376
|
+
const selected = readSetupStatus(home), installation = selected.installation;
|
|
377
|
+
if (!installation || selected.installation_readiness !== "installed" || installation.manifest_sha256 !== raw.target_installation_sha256)
|
|
378
|
+
refuse("target_changed");
|
|
379
|
+
const git = inspectMemoryGitExecutable(options.env);
|
|
380
|
+
const policy = observeMemoryGitPolicySelection(options.env, { scope, gitExecutable: git.executable, selection: raw.git_policy });
|
|
381
|
+
const baseline = current(home, scope, layer, options.env, undefined, git.executable);
|
|
382
|
+
if (policy.workspace_branch !== baseline.workspace.branch || policy.initial_head !== baseline.workspace.head)
|
|
383
|
+
refuse("git_selection_changed");
|
|
384
|
+
const payload = { schema_id: "dd-flow/memory-upgrade-selection@1", scope: { ...scope }, request_input_sha256: hybridDigest(original),
|
|
385
|
+
...baseline, git: { ...git, policy }, impact: impacts(layer, baseline.current.canon_version, source.canonProvenance().version), source: { sha256: source.sha256, snapshot: source.snapshot() },
|
|
386
|
+
release: { manifest_sha256: installation.manifest_sha256, manifest_base64: installation.manifest_bytes_base64 },
|
|
387
|
+
layer: { sha256: layer.sha256, installation_sha256: layer.installationSha256, files: [...layer.files()].map(([logical, content]) => ({ path: logical, base64: content.toString("base64") })) },
|
|
388
|
+
execution: { sha256: execution.sha256, snapshot: execution.snapshot(), receipts: selectedReceipts, receipt_blobs: receiptBlobs },
|
|
389
|
+
engine: { identity: { package_name: manifest.package_name, package_version: manifest.package_version, engine_version: manifest.engine_version,
|
|
390
|
+
snapshot_root: manifest.snapshot_root, integrity_checksum: manifest.integrity.checksum }, manifest_sha256: engine.manifestSha256, release_pairing: pairing } };
|
|
391
|
+
assertMixedCurrentContext(payload.current, payload.impact);
|
|
392
|
+
return new PreparedMemoryUpgradeSelection(home, candidate, payload, source, layer, engine, options.env);
|
|
393
|
+
}
|
|
394
|
+
retain() {
|
|
395
|
+
const p = this.#payload, prior = readMemoryUpgradeSelection(this.#home, p.scope);
|
|
396
|
+
if (prior) {
|
|
397
|
+
if (prior.sha256 !== this.sha256)
|
|
398
|
+
refuse("selection_changed");
|
|
399
|
+
return { sha256: this.sha256, changed: false };
|
|
400
|
+
}
|
|
401
|
+
assertImportAdmission(this.#home);
|
|
402
|
+
if (hybridDigest(captureMemoryUpgradeRequestCandidate(this.#candidate, this.#home, p.scope, this.#candidate.objective)) !== p.request_input_sha256)
|
|
403
|
+
refuse("request_changed");
|
|
404
|
+
this.#source.revalidate();
|
|
405
|
+
if (p.git) {
|
|
406
|
+
const raw = readMemoryUpgradeRequestInput(captureMemoryUpgradeRequestCandidate(this.#candidate, this.#home, p.scope, this.#candidate.objective), p.scope, this.#candidate.objective).selection;
|
|
407
|
+
if (hash(readBoundedReleaseFile(p.git.executable, 64 * 1024 * 1024)) !== p.git.probe.executable_sha256
|
|
408
|
+
|| hybridDigest(observeMemoryGitPolicySelection(this.#env, { scope: p.scope, gitExecutable: p.git.executable, selection: raw.git_policy })) !== hybridDigest(p.git.policy))
|
|
409
|
+
refuse("git_selection_changed");
|
|
410
|
+
}
|
|
411
|
+
const installedAt = JSON.parse(this.#layer.files().get("dd-flow/manifest.json").toString("utf8")).installed_at;
|
|
412
|
+
if (PreparedMemoryLayer.fromInstalledRelease(this.#home, this.#layer.installationSha256, installedAt).sha256 !== this.#layer.sha256
|
|
413
|
+
|| hybridDigest(current(this.#home, p.scope, this.#layer, this.#env, p.current.baseline_layer?.installation_sha256 ?? null, p.git?.executable, p.current.project_links !== undefined, p.current.project_files !== undefined)) !== hybridDigest({ current: p.current, workspace: p.workspace })
|
|
414
|
+
|| hybridDigest(impacts(this.#layer, p.current.canon_version, this.#source.canonProvenance().version)) !== hybridDigest(p.impact))
|
|
415
|
+
refuse("selection_changed_before_retention");
|
|
416
|
+
revalidateMemoryInitEngineRelease(p.engine.release_pairing.facts, p.engine.release_pairing.sha256, this.#engine);
|
|
417
|
+
const locations = paths(this.#candidate);
|
|
418
|
+
try {
|
|
419
|
+
publishImmutableInput(locations.receipt, Buffer.from(JSON.stringify({ schema_id: "dd-flow/memory-upgrade-selection-receipt@1", scope: p.scope,
|
|
420
|
+
request_input_sha256: p.request_input_sha256, selection_sha256: this.sha256 })), "memory_upgrade_selection_conflict");
|
|
421
|
+
publishImmutableInput(locations.payload, this.#bytes, "memory_upgrade_selection_conflict");
|
|
422
|
+
if (readMemoryUpgradeSelection(this.#home, p.scope)?.sha256 !== this.sha256)
|
|
423
|
+
refuse("selection_readback_failed");
|
|
424
|
+
return { sha256: this.sha256, changed: true };
|
|
425
|
+
}
|
|
426
|
+
catch (error) {
|
|
427
|
+
throw new AppError("memory_upgrade_selection_incomplete", "Upgrade selection requires exact request reconciliation", 1, { effect: "partial", request_id: p.scope.requestId, cause: errorRecord(error), next_action: "reconcile_exact_request" });
|
|
428
|
+
}
|
|
429
|
+
}
|
|
430
|
+
}
|
|
@@ -0,0 +1,60 @@
|
|
|
1
|
+
import { AppError } from "../shared/errors.js";
|
|
2
|
+
import { PreparedDescriptorRunStart } from "./descriptor-run-start.js";
|
|
3
|
+
import { readRunStartRequest } from "./run-start-custody.js";
|
|
4
|
+
import { PreparedMemoryUpgradeBootstrapReadiness } from "./memory-upgrade-bootstrap-readiness.js";
|
|
5
|
+
import { readUpgradeStorageReadiness, assertMemoryUpgradeProjectCurrent, assertUpgradeExecutionStorage } from "./memory-upgrade-storage-readiness.js";
|
|
6
|
+
import { restoreMemoryUpgradeSelection, restoreMemoryUpgradeSelectionData } from "./memory-upgrade-selection.js";
|
|
7
|
+
import { PreparedProjectRegistration, registerPreparedProject } from "./projects.js";
|
|
8
|
+
import { prepareMemoryUpgradeRunBundle } from "./memory-init-run-bundle.js";
|
|
9
|
+
import { validateMemoryInitStartupScope } from "./memory-init-startup-custody.js";
|
|
10
|
+
function refuse(reason) {
|
|
11
|
+
throw new AppError("memory_upgrade_start_invalid", "Upgrade RUN requires its retained request and accepted storage", 1, { reason, effect: "no_effect", next_action: "reconcile_exact_request" });
|
|
12
|
+
}
|
|
13
|
+
/** Register only metadata for an existing MB after target storage admission.
|
|
14
|
+
* Never use init's draft publication or revive an archived project. */
|
|
15
|
+
export function registerMemoryUpgradeProject(context, scope) {
|
|
16
|
+
const existing = context.db.get("SELECT status FROM projects WHERE root=?", [scope.projectRoot]);
|
|
17
|
+
if (existing) {
|
|
18
|
+
if (existing.status !== "active")
|
|
19
|
+
refuse("project_not_active");
|
|
20
|
+
return;
|
|
21
|
+
}
|
|
22
|
+
const selected = restoreMemoryUpgradeSelection(context.ddFlowHome, scope);
|
|
23
|
+
assertMemoryUpgradeProjectCurrent(selected, scope, context.env);
|
|
24
|
+
assertUpgradeExecutionStorage(context, selected.engine);
|
|
25
|
+
const registration = PreparedProjectRegistration.prepare({ root: scope.projectRoot });
|
|
26
|
+
context.db.writeTransaction(() => {
|
|
27
|
+
const current = context.db.get("SELECT status FROM projects WHERE root=?", [scope.projectRoot]);
|
|
28
|
+
if (current) {
|
|
29
|
+
if (current.status !== "active")
|
|
30
|
+
refuse("project_not_active");
|
|
31
|
+
return;
|
|
32
|
+
}
|
|
33
|
+
registerPreparedProject(context, registration);
|
|
34
|
+
}, "memoryUpgrade.registerProject");
|
|
35
|
+
}
|
|
36
|
+
/** Storage admission precedes this operation. The existing RUN owner handles
|
|
37
|
+
* allocation/publication and exact retries; no Session or agent is dispatched.
|
|
38
|
+
* Restore a published request before observing mutable project/tool state. */
|
|
39
|
+
export function startMemoryUpgradeRun(context, scope) {
|
|
40
|
+
validateMemoryInitStartupScope(scope);
|
|
41
|
+
if (context.db.isOwnedWriteTransaction?.())
|
|
42
|
+
refuse("startup_outside_writer_required");
|
|
43
|
+
const project = context.db.get("SELECT id FROM projects WHERE root=? AND status='active'", [scope.projectRoot]);
|
|
44
|
+
if (!project)
|
|
45
|
+
refuse("project_not_active");
|
|
46
|
+
const request = readRunStartRequest(context, project.id, scope.requestId);
|
|
47
|
+
if (request) {
|
|
48
|
+
if (request.payload.project_root !== scope.projectRoot || request.payload.workspace_root !== scope.workspaceRoot
|
|
49
|
+
|| request.payload.initial_index.flow_kind !== "mb-upgrade")
|
|
50
|
+
refuse("run_request_changed");
|
|
51
|
+
return PreparedDescriptorRunStart.restore(context, { projectRoot: scope.projectRoot, requestId: scope.requestId }).apply(context);
|
|
52
|
+
}
|
|
53
|
+
const storage = readUpgradeStorageReadiness(context, scope);
|
|
54
|
+
if (!storage?.receipt.selection)
|
|
55
|
+
refuse("accepted_storage_required");
|
|
56
|
+
PreparedMemoryUpgradeBootstrapReadiness.prepareForRun(context, scope).apply(context);
|
|
57
|
+
const retained = storage.receipt.selection;
|
|
58
|
+
const selected = restoreMemoryUpgradeSelectionData(Buffer.from(retained.payload_json), retained.sha256, retained.request_input, scope);
|
|
59
|
+
return PreparedDescriptorRunStart.prepare(context, { bundle: prepareMemoryUpgradeRunBundle(context, selected) }).apply(context);
|
|
60
|
+
}
|