@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
|
@@ -5,9 +5,14 @@ import { Ajv } from "ajv/dist/ajv.js";
|
|
|
5
5
|
import semver from "semver";
|
|
6
6
|
import { AppError } from "../shared/errors.js";
|
|
7
7
|
import { hybridDigest } from "./hybrid-contracts.js";
|
|
8
|
-
import { readSetupStatus } from "./setup.js";
|
|
8
|
+
import { readSetupStatus, readCommittedCanonRelease } from "./setup.js";
|
|
9
9
|
import { readBoundedReleaseFile } from "./release-source.js";
|
|
10
10
|
import { normalizeFlowContract } from "../domain/flow-contract.js";
|
|
11
|
+
import { inspectReleaseArchive } from "./release-archive.js";
|
|
12
|
+
import { canonicalOnlyFlowNames } from "./version-status.js";
|
|
13
|
+
export function isCanonicalOnlyMemoryPath(logical) {
|
|
14
|
+
return canonicalOnlyFlowNames.some(name => logical === `dd-flow/${name}` || logical.startsWith(`dd-flow/${name.slice(0, -3)}/`));
|
|
15
|
+
}
|
|
11
16
|
const rootFiles = ["README.md", "index.md", "compatibility.json", "flow-contract.json", "go.md", "f.md", "prime.md", "protocol.md",
|
|
12
17
|
"protocol-implement.md", "plan.md", "code.md", "merge.md", "merge-start.md", "merge-stop.md", "release.md", "deploy.md", "publish.md",
|
|
13
18
|
"review.md", "review-fix.md", "mb-audit.md", "mb-fix.md", "mb-lint.md", "interactive.md", "finish.md", "fix-def.md"];
|
|
@@ -112,7 +117,7 @@ export class PreparedMemoryLayer {
|
|
|
112
117
|
source_commit: installation.manifest.canon.commit, canon_version_at_source_commit: installation.manifest.canon.version,
|
|
113
118
|
flow_contract: contract.id, installed_at: installedAt, included_files: included,
|
|
114
119
|
file_hashes: Object.fromEntries(included.map(logical => [logical, hash(files.get(`dd-flow/${logical}`))])),
|
|
115
|
-
excluded_canonical_only_flows:
|
|
120
|
+
excluded_canonical_only_flows: canonicalOnlyFlowNames, generated_allow_patterns: [".generated/**"] };
|
|
116
121
|
const schema = JSON.parse(fs.readFileSync(new URL("../schemas/project-flow-pack-manifest.schema.json", import.meta.url), "utf8"));
|
|
117
122
|
if (!new Ajv({ strict: false, validateFormats: false }).compile(schema)(manifest))
|
|
118
123
|
refuse("invalid_pack_manifest");
|
|
@@ -130,10 +135,69 @@ export class PreparedMemoryLayer {
|
|
|
130
135
|
}
|
|
131
136
|
}
|
|
132
137
|
files() { return new Map([...this.#files].map(([logical, bytes]) => [logical, Buffer.from(bytes)])); }
|
|
138
|
+
/** Historical content baseline, not an executable target layer. Project
|
|
139
|
+
* paths select a subset; every byte/provenance fact comes from the committed
|
|
140
|
+
* setup canon. Old runtime compatibility and current installation defaults
|
|
141
|
+
* are irrelevant, and no release is activated or downloaded here.
|
|
142
|
+
*/
|
|
143
|
+
static fromCommittedCanon(home, current, expectedInstallationSha256) {
|
|
144
|
+
if (!path.isAbsolute(home) || !current || !Array.isArray(current.included_files) || current.included_files.length > 20_000
|
|
145
|
+
|| typeof current.installed_at !== "string" || !current.installed_at || current.installed_at.length > 256)
|
|
146
|
+
refuse("invalid_baseline_selection");
|
|
147
|
+
const installation = readCommittedCanonRelease(home, current.canon_version_at_source_commit, current.source_commit, expectedInstallationSha256);
|
|
148
|
+
if (!installation)
|
|
149
|
+
return null;
|
|
150
|
+
const archive = inspectReleaseArchive(installation.manifest.canon.archive, readBoundedReleaseFile(path.join(path.dirname(installation.canon_root), "archive.tgz"), installation.manifest.canon.archive.bytes));
|
|
151
|
+
const roots = [...new Set(archive.map(entry => entry.path.split("/")[0]))];
|
|
152
|
+
if (roots.length !== 1)
|
|
153
|
+
refuse("ambiguous_archive_root");
|
|
154
|
+
const prefix = archive.some(entry => entry.path.startsWith(`${roots[0]}/.memory-bank/`)) ? `${roots[0]}/.memory-bank` : roots[0];
|
|
155
|
+
const canonRoot = path.join(installation.canon_root, roots[0]), memoryRoot = path.join(installation.canon_root, prefix);
|
|
156
|
+
const sourceFiles = new Map(archive.filter(entry => entry.kind === "file").map(entry => [entry.path, entry.bytes]));
|
|
157
|
+
const files = new Map(), folded = new Set();
|
|
158
|
+
let total = 0;
|
|
159
|
+
const put = (logical, content) => {
|
|
160
|
+
if (!/^(?:dd-flow|mbb)\/[A-Za-z0-9._-]+(?:\/[A-Za-z0-9._-]+)*$/.test(logical)
|
|
161
|
+
|| content.length > 1024 * 1024 || files.size >= 20_000 || (total += content.length) > 64 * 1024 * 1024
|
|
162
|
+
|| folded.has(logical.toLowerCase()))
|
|
163
|
+
refuse("unsafe_or_unbounded_baseline");
|
|
164
|
+
folded.add(logical.toLowerCase());
|
|
165
|
+
files.set(logical, Buffer.from(content));
|
|
166
|
+
};
|
|
167
|
+
const flowRoot = path.join(memoryRoot, "dd-flow");
|
|
168
|
+
for (const logical of [...new Set(current.included_files)].sort()) {
|
|
169
|
+
if (typeof logical !== "string" || !/^[A-Za-z0-9._-]+(?:\/[A-Za-z0-9._-]+)*$/.test(logical)
|
|
170
|
+
|| logical.split("/").some(part => [".", ".."].includes(part)))
|
|
171
|
+
refuse("unsafe_baseline_path");
|
|
172
|
+
if (logical === "manifest.json")
|
|
173
|
+
continue;
|
|
174
|
+
const content = sourceFiles.get(`${prefix}/dd-flow/${logical}`);
|
|
175
|
+
if (content)
|
|
176
|
+
put(`dd-flow/${logical}`, content);
|
|
177
|
+
}
|
|
178
|
+
const included = [...files.keys()].map(logical => logical.slice("dd-flow/".length));
|
|
179
|
+
if (!included.length)
|
|
180
|
+
return null;
|
|
181
|
+
for (const [logical, content] of sourceFiles) {
|
|
182
|
+
const relative = logical.slice(prefix.length + 1);
|
|
183
|
+
if (logical.startsWith(`${prefix}/`) && (relative.startsWith("mbb/") || isCanonicalOnlyMemoryPath(relative)))
|
|
184
|
+
put(relative, content);
|
|
185
|
+
}
|
|
186
|
+
const manifestBytes = Buffer.from(JSON.stringify({ schema_id: "dd-flow/project-flow-pack-manifest@1",
|
|
187
|
+
pack_version: installation.manifest.canon.version, flow_origin_policy: "project_local_pack", canon_root: canonRoot,
|
|
188
|
+
canon_memory_bank_root: memoryRoot, canon_flow_root: flowRoot, source_commit: installation.manifest.canon.commit,
|
|
189
|
+
canon_version_at_source_commit: installation.manifest.canon.version, installed_at: current.installed_at,
|
|
190
|
+
included_files: included, file_hashes: Object.fromEntries(included.map(logical => [logical, hash(files.get(`dd-flow/${logical}`))])),
|
|
191
|
+
excluded_canonical_only_flows: canonicalOnlyFlowNames }));
|
|
192
|
+
if (files.size >= 20_000 || manifestBytes.length > 1024 * 1024 || total + manifestBytes.length > 64 * 1024 * 1024)
|
|
193
|
+
refuse("unsafe_or_unbounded_baseline");
|
|
194
|
+
files.set("dd-flow/manifest.json", manifestBytes);
|
|
195
|
+
return new PreparedMemoryLayer(new Map([...files].sort(([left], [right]) => left.localeCompare(right, "en"))), installation.manifest_sha256);
|
|
196
|
+
}
|
|
133
197
|
/** Restore only with an independently retained hash of an already admitted layer.
|
|
134
198
|
* SHA admission proves the original verified bytes; mutable installation sources are not read.
|
|
135
199
|
*/
|
|
136
|
-
static restoreRetained(files, expectedSha256, installationSha256) {
|
|
200
|
+
static restoreRetained(files, expectedSha256, installationSha256, allowEmpty = false) {
|
|
137
201
|
if (!/^[a-f0-9]{64}$/.test(expectedSha256) || !/^[a-f0-9]{64}$/.test(installationSha256)
|
|
138
202
|
|| files.size < 1 || files.size > 20_000)
|
|
139
203
|
refuse("invalid_retained_selection");
|
|
@@ -142,7 +206,7 @@ export class PreparedMemoryLayer {
|
|
|
142
206
|
for (const [logical, content] of files) {
|
|
143
207
|
if (!/^(?:mbb|dd-flow)\/[A-Za-z0-9._-]+(?:\/[A-Za-z0-9._-]+)*$/.test(logical)
|
|
144
208
|
|| logical.split("/").some(segment => segment === "." || segment === "..")
|
|
145
|
-
|| folded.has(logical.toLowerCase()) || content.length < 1 || content.length > 1024 * 1024
|
|
209
|
+
|| folded.has(logical.toLowerCase()) || !allowEmpty && content.length < 1 || content.length > 1024 * 1024
|
|
146
210
|
|| (total += content.length) > 64 * 1024 * 1024)
|
|
147
211
|
refuse("unsafe_or_unbounded_inventory");
|
|
148
212
|
folded.add(logical.toLowerCase());
|
|
@@ -1,10 +1,101 @@
|
|
|
1
1
|
import { AppError } from "../shared/errors.js";
|
|
2
|
+
import { createHash } from "node:crypto";
|
|
3
|
+
import { parseDocument } from "yaml";
|
|
4
|
+
/** Source templates are assets, not instantiated project documents. A known
|
|
5
|
+
* canonical asset may retain customized placement or a fixed example date;
|
|
6
|
+
* its header must still be a template. A directory name grants no exemption. */
|
|
7
|
+
export function memoryCanonicalTemplatePaths(entries, files, current) {
|
|
8
|
+
const result = new Set();
|
|
9
|
+
const placeholder = (value) => typeof value === "string" && /<[^>]+>/.test(value);
|
|
10
|
+
const header = (bytes) => {
|
|
11
|
+
const match = new TextDecoder("utf-8", { fatal: true }).decode(bytes).match(/^---\r?\n([\s\S]*?)\r?\n---(?:\r?\n|$)/);
|
|
12
|
+
if (!match)
|
|
13
|
+
return null;
|
|
14
|
+
const document = parseDocument(match[1]);
|
|
15
|
+
return document.errors.length ? null : document.toJS();
|
|
16
|
+
};
|
|
17
|
+
for (const entry of entries) {
|
|
18
|
+
if (!/^mbb\/templates\/.+\.(?:md|markdown)$/.test(entry.path) || !entry.target || !["canonical", "local-customized"].includes(entry.ownership))
|
|
19
|
+
continue;
|
|
20
|
+
const target = files.get(entry.path);
|
|
21
|
+
if (!target || createHash("sha256").update(target).digest("hex") !== entry.target.sha256)
|
|
22
|
+
refuse("canonical_template_source_changed");
|
|
23
|
+
const bytes = current.get(entry.path);
|
|
24
|
+
if (!bytes)
|
|
25
|
+
continue;
|
|
26
|
+
try {
|
|
27
|
+
const source = header(target), observed = header(bytes);
|
|
28
|
+
if (source && observed && typeof observed.date === "string" && /^(?:YYYY-MM-DD|\d{4}-\d{2}-\d{2})$/.test(observed.date)
|
|
29
|
+
&& ["file", "description", "purpose"].some(key => placeholder(source[key]) && placeholder(observed[key])))
|
|
30
|
+
result.add(`.memory-bank/${entry.path}`);
|
|
31
|
+
}
|
|
32
|
+
catch { /* Malformed headers remain active and are linted. */ }
|
|
33
|
+
}
|
|
34
|
+
return result;
|
|
35
|
+
}
|
|
36
|
+
/** A copied canonical guide may name its source repository's specs/protocols.
|
|
37
|
+
* Only those exact frozen relations are foreign provenance, not a promise that
|
|
38
|
+
* the consumer contains them. New project targets and body links stay checked. */
|
|
39
|
+
export function memoryCanonicalLintExceptions(entries, files, selected) {
|
|
40
|
+
const result = [];
|
|
41
|
+
for (const file of new Set(selected)) {
|
|
42
|
+
if (!/^\.memory-bank\/(?:dd-flow|mbb)\/.+\.(?:md|markdown)$/.test(file))
|
|
43
|
+
continue;
|
|
44
|
+
if (["*", "?", "[", "]", "{", "}", "!", "(", ")", "\\"].some(character => file.includes(character)))
|
|
45
|
+
continue;
|
|
46
|
+
const logical = file.slice(13), entry = entries.find(entry => entry.path === logical);
|
|
47
|
+
if (!entry?.target || !["canonical", "local-customized"].includes(entry.ownership))
|
|
48
|
+
continue;
|
|
49
|
+
const bytes = files.get(logical);
|
|
50
|
+
if (!bytes || createHash("sha256").update(bytes).digest("hex") !== entry.target.sha256)
|
|
51
|
+
refuse("canonical_guidance_source_changed");
|
|
52
|
+
let header;
|
|
53
|
+
try {
|
|
54
|
+
const match = new TextDecoder("utf-8", { fatal: true }).decode(bytes).match(/^---\r?\n([\s\S]*?)\r?\n---(?:\r?\n|$)/);
|
|
55
|
+
if (!match)
|
|
56
|
+
continue;
|
|
57
|
+
const document = parseDocument(match[1]);
|
|
58
|
+
if (document.errors.length)
|
|
59
|
+
continue;
|
|
60
|
+
const value = document.toJS();
|
|
61
|
+
if (!value || typeof value !== "object" || Array.isArray(value) || value.file !== file)
|
|
62
|
+
continue;
|
|
63
|
+
header = value;
|
|
64
|
+
}
|
|
65
|
+
catch {
|
|
66
|
+
continue;
|
|
67
|
+
}
|
|
68
|
+
const targets = new Set(["related_specs", "related_protocols"].flatMap(key => Array.isArray(header[key]) ? header[key] : [])
|
|
69
|
+
.filter((target) => typeof target === "string" && /^\.memory-bank\/(?:spec|protocol)\//.test(target)
|
|
70
|
+
&& !["*", "?", "[", "]", "{", "}", "!", "(", ")"].some(character => target.includes(character))
|
|
71
|
+
&& !target.includes("\\") && !target.includes("\0") && target.split("/").every(part => part && part !== "." && part !== "..")));
|
|
72
|
+
for (const target of targets)
|
|
73
|
+
result.push({ ruleId: "links/frontmatter-target-exists", file, target,
|
|
74
|
+
reason: "Source-canon spec/protocol provenance, not a required consumer-owned document; source resolution is not claimed." });
|
|
75
|
+
}
|
|
76
|
+
return result;
|
|
77
|
+
}
|
|
78
|
+
/** Pure ownership projection from retained inventory and accepted diff. Exact
|
|
79
|
+
* retained-only bytes keep their existing lint policy; they are preservation
|
|
80
|
+
* artifacts, not new authoring. Changed or semantic-work files stay protected.
|
|
81
|
+
* Mechanical canonical references remain verified by their publication owner. */
|
|
82
|
+
export function memoryUpgradeLintGuidance(entries, changes) {
|
|
83
|
+
const authored = new Set(changes.filter(change => change.action === "semantic-work").map(change => change.to));
|
|
84
|
+
const retained = new Set(changes.filter(change => change.action === "retain" && change.from === change.to).map(change => change.from));
|
|
85
|
+
const preserved = entries.filter(entry => entry.current?.kind === "file" && /\.(?:md|markdown)$/.test(entry.path)
|
|
86
|
+
&& retained.has(`.memory-bank/${entry.path}`) && !authored.has(`.memory-bank/${entry.path}`))
|
|
87
|
+
.map(entry => [`.memory-bank/${entry.path}`, entry.current.sha256]);
|
|
88
|
+
return new Map([...preserved, ...entries.filter(entry => entry.ownership === "canonical" && entry.target
|
|
89
|
+
&& /^(?:dd-flow|mbb)\//.test(entry.path) && !authored.has(`.memory-bank/${entry.path}`))
|
|
90
|
+
.map(entry => [`.memory-bank/${entry.path}`, entry.target.sha256])]);
|
|
91
|
+
}
|
|
2
92
|
/** Review inputs also contain immutable guidance; only accepted current unit
|
|
3
93
|
* artifacts are project output that lint configuration must not suppress. */
|
|
4
|
-
export function memoryVerificationActivePaths(units, assignments = []) {
|
|
94
|
+
export function memoryVerificationActivePaths(units, assignments = [], guidance, templates) {
|
|
5
95
|
return [".memory-bank/index.md", ".memory-bank/structure.md", ".memory-bank/project-policy.md",
|
|
6
96
|
...units.flatMap(unit => (assignments.find(assignment => assignment.unit.id === unit.id)?.unit ?? unit).artifacts
|
|
7
|
-
.filter(artifact => artifact.root === "workspace")
|
|
97
|
+
.filter(artifact => artifact.root === "workspace" && !templates?.has(artifact.path) && !(typeof artifact.sha256 === "string" && guidance?.get(artifact.path) === artifact.sha256))
|
|
98
|
+
.map(artifact => artifact.path))];
|
|
8
99
|
}
|
|
9
100
|
/** Bootstrap-owned policy for immutable guidance, never project knowledge or
|
|
10
101
|
* the two project-owned JSON defaults beside the canonical flow pack. */
|
|
@@ -83,3 +174,21 @@ export function validateMemoryLintConfig(bytes, activePaths) {
|
|
|
83
174
|
refuse("invalid_git_config");
|
|
84
175
|
}
|
|
85
176
|
}
|
|
177
|
+
/** Preserve project lint policy, but independently check every active Markdown
|
|
178
|
+
* document it suppresses. JSON artifacts belong to their schema owners, not
|
|
179
|
+
* mb-lint's Markdown scanner. Invalid policy still refuses instead of fallback. */
|
|
180
|
+
export function memoryVerificationSupplementalLintFiles(bytes, activePaths) {
|
|
181
|
+
validateMemoryLintConfig(bytes, []);
|
|
182
|
+
return [...new Set(activePaths)].filter(file => file.startsWith(".memory-bank/") && /\.(?:md|markdown)$/.test(file)).filter(file => {
|
|
183
|
+
try {
|
|
184
|
+
validateMemoryLintConfig(bytes, [file]);
|
|
185
|
+
return false;
|
|
186
|
+
}
|
|
187
|
+
catch (error) {
|
|
188
|
+
if (error instanceof AppError && error.code === "memory_lint_config_invalid"
|
|
189
|
+
&& ["active_document_ignored", "active_finding_suppressed"].includes(String(error.details.reason)))
|
|
190
|
+
return true;
|
|
191
|
+
throw error;
|
|
192
|
+
}
|
|
193
|
+
});
|
|
194
|
+
}
|
|
@@ -1,6 +1,5 @@
|
|
|
1
1
|
import fs from "node:fs";
|
|
2
2
|
import path from "node:path";
|
|
3
|
-
import { createHash } from "node:crypto";
|
|
4
3
|
import { AppError } from "../shared/errors.js";
|
|
5
4
|
import { appendAudit } from "./audit.js";
|
|
6
5
|
import { hybridDigest } from "./hybrid-contracts.js";
|
|
@@ -8,70 +7,9 @@ import { prepareMemoryReviewWorkInput, assertPreparedMemoryReviewWorkInput } fro
|
|
|
8
7
|
import { assertRunMutationAllowed } from "./run-recovery-guard.js";
|
|
9
8
|
import { reviewSourceFilter, reviewSourceHash, reviewExecutionCopyHash } from "./review-copy.js";
|
|
10
9
|
import { writeReviewGitEvidence } from "./external-work-launch.js";
|
|
10
|
+
import { memoryReviewPhysicalDestination } from "./hook-memory-routing.js";
|
|
11
11
|
function refuse(reason) { throw new AppError("memory_native_review_copy_invalid", "Native Memory review requires an independently owned immutable execution copy", 1, { reason, effect: "no_effect" }); }
|
|
12
|
-
|
|
13
|
-
if (fs.realpathSync(runRoot) !== path.resolve(runRoot) || !fs.lstatSync(runRoot).isDirectory())
|
|
14
|
-
refuse("noncanonical_run_root");
|
|
15
|
-
let current = runRoot;
|
|
16
|
-
for (const part of relative.split("/")) {
|
|
17
|
-
if (!part || part === "." || part === "..")
|
|
18
|
-
refuse("invalid_copy_path");
|
|
19
|
-
current = path.join(current, part);
|
|
20
|
-
const stat = fs.lstatSync(current, { throwIfNoEntry: false });
|
|
21
|
-
if (stat && !stat.isDirectory())
|
|
22
|
-
refuse("linked_or_non_directory_copy_path");
|
|
23
|
-
if (!stat)
|
|
24
|
-
break;
|
|
25
|
-
}
|
|
26
|
-
}
|
|
27
|
-
/** Routing is not fresh source admission: an already claimed reviewer retains
|
|
28
|
-
* its committed private directory even after a later correction changes MB. */
|
|
29
|
-
export function memoryNativeReviewAssignedHookCwd(context, input) {
|
|
30
|
-
const work = context.db.get("SELECT status,payload_json FROM works WHERE project_id=? AND run_id=? AND work_id=?", [input.projectId, input.runId, input.workId]);
|
|
31
|
-
const payload = JSON.parse(work?.payload_json ?? "null");
|
|
32
|
-
if (payload?.kind !== "memory-review" || payload.read_only !== true)
|
|
33
|
-
return null;
|
|
34
|
-
const ready = context.db.get("SELECT 1 FROM audit_events WHERE project_id=? AND event_type='memory.native_review_copy_ready' AND json_extract(payload_json,'$.intent.run_id')=? AND json_extract(payload_json,'$.intent.work_id')=?", [input.projectId, input.runId, input.workId]);
|
|
35
|
-
if (!ready)
|
|
36
|
-
return null;
|
|
37
|
-
if (work?.status === "created") {
|
|
38
|
-
// Hook ingress routes an already allocated copy. Source admission belongs
|
|
39
|
-
// to the actual work-start owner, not a second physical graph in the Hook.
|
|
40
|
-
const generation = assertRunMutationAllowed(context, input.projectId, input.runId);
|
|
41
|
-
const run = context.db.get("SELECT run_root,run_dir FROM runs WHERE project_id=? AND id=?", [input.projectId, input.runId]);
|
|
42
|
-
if (!run)
|
|
43
|
-
refuse("owned_run_required");
|
|
44
|
-
const rows = (event) => context.db.all("SELECT payload_json FROM audit_events WHERE project_id=? AND event_type=? AND COALESCE(json_extract(payload_json,'$.run_id'),json_extract(payload_json,'$.intent.run_id'))=? AND COALESCE(json_extract(payload_json,'$.work_id'),json_extract(payload_json,'$.intent.work_id'))=? AND COALESCE(json_extract(payload_json,'$.generation'),json_extract(payload_json,'$.intent.generation'))=? LIMIT 2", [input.projectId, event, input.runId, input.workId, generation]);
|
|
45
|
-
const intents = rows("memory.native_review_copy_intent"), copies = rows("memory.native_review_copy_ready");
|
|
46
|
-
if (intents.length !== 1 || copies.length !== 1)
|
|
47
|
-
refuse("registered_copy_required");
|
|
48
|
-
const intent = JSON.parse(intents[0].payload_json), copy = JSON.parse(copies[0].payload_json);
|
|
49
|
-
const relative = `works/${input.workId}/native-review-workspaces/generation-${generation}`, runRoot = run.run_root ?? run.run_dir;
|
|
50
|
-
if (intent.schema_id !== "dd-flow/memory-native-review-copy@1" || intent.project_id !== input.projectId || intent.run_id !== input.runId
|
|
51
|
-
|| intent.work_id !== input.workId || intent.generation !== generation || intent.root !== relative
|
|
52
|
-
|| !/^[a-f0-9]{64}$/.test(intent.input_sha256) || !/^[a-f0-9]{64}$/.test(intent.source_sha256)
|
|
53
|
-
|| hybridDigest(copy.intent) !== hybridDigest(intent) || !/^[a-f0-9]{64}$/.test(copy.sha256))
|
|
54
|
-
refuse("registered_copy_changed");
|
|
55
|
-
physicalDestination(runRoot, relative);
|
|
56
|
-
const root = path.join(runRoot, relative);
|
|
57
|
-
if (!fs.lstatSync(root, { throwIfNoEntry: false })?.isDirectory())
|
|
58
|
-
refuse("registered_copy_required");
|
|
59
|
-
return root;
|
|
60
|
-
}
|
|
61
|
-
const rows = context.db.all("SELECT ws.start_receipt_json,r.run_root,r.run_dir,s.cwd FROM work_sessions ws JOIN works w ON w.work_id=ws.work_id JOIN runs r ON r.project_id=w.project_id AND r.id=w.run_id JOIN sessions s ON s.project_id=w.project_id AND s.session_id=ws.session_id WHERE w.project_id=? AND w.run_id=? AND w.work_id=? AND s.provider_session_id=? AND s.worker_id=w.work_id AND EXISTS(SELECT 1 FROM audit_events a WHERE a.project_id=w.project_id AND a.event_type='memory.native_review_binding' AND json_extract(a.payload_json,'$.work_session_id')=ws.id AND json_extract(a.payload_json,'$.run_id')=w.run_id AND json_extract(a.payload_json,'$.work_id')=w.work_id) LIMIT 2", [input.projectId, input.runId, input.workId, input.providerSessionId]);
|
|
62
|
-
if (rows.length !== 1)
|
|
63
|
-
refuse("registered_session_required");
|
|
64
|
-
const row = rows[0];
|
|
65
|
-
if (typeof row.start_receipt_json !== "string" || Buffer.byteLength(row.start_receipt_json) > 16 * 1024 * 1024)
|
|
66
|
-
refuse("registered_copy_required");
|
|
67
|
-
const receipt = JSON.parse(row.start_receipt_json), packet = JSON.parse(receipt.context);
|
|
68
|
-
const relative = `works/${input.workId}/native-review-workspaces/generation-${receipt.generation}`, root = path.join(row.run_root ?? row.run_dir, relative);
|
|
69
|
-
if (receipt.version !== 2 || createHash("sha256").update(receipt.context).digest("hex") !== receipt.context_sha256
|
|
70
|
-
|| packet.work_id !== input.workId || packet.run_id !== input.runId || packet.workspace_root !== root
|
|
71
|
-
|| packet.review_copy?.root !== relative || row.cwd !== root)
|
|
72
|
-
refuse("registered_copy_changed");
|
|
73
|
-
return root;
|
|
74
|
-
}
|
|
12
|
+
export { memoryNativeReviewAssignedHookCwd } from "./hook-memory-routing.js";
|
|
75
13
|
/** Existing source/copy policy, separate native physical custody: no external
|
|
76
14
|
* launch row is invented. An interrupted allocation is never overwritten. */
|
|
77
15
|
export function prepareMemoryNativeReviewCopy(context, workId, allocate = false, preparedOwner) {
|
|
@@ -89,7 +27,7 @@ export function prepareMemoryNativeReviewCopy(context, workId, allocate = false,
|
|
|
89
27
|
refuse("native_route_required");
|
|
90
28
|
const relative = `works/${workId}/native-review-workspaces/generation-${generation}`;
|
|
91
29
|
const runRoot = run.run_root ?? run.run_dir, root = path.join(runRoot, relative);
|
|
92
|
-
|
|
30
|
+
memoryReviewPhysicalDestination(runRoot, relative);
|
|
93
31
|
const inputs = owner.input.run_inputs.filter(ref => ref.root === "workspace").map(ref => ref.path);
|
|
94
32
|
const selectedBase = JSON.parse(run.index_json).workspace?.base;
|
|
95
33
|
const base = selectedBase && selectedBase !== "unknown" ? selectedBase : "HEAD";
|
|
@@ -125,9 +63,9 @@ export function prepareMemoryNativeReviewCopy(context, workId, allocate = false,
|
|
|
125
63
|
});
|
|
126
64
|
try {
|
|
127
65
|
fs.mkdirSync(path.dirname(root), { recursive: true, mode: 0o700 });
|
|
128
|
-
|
|
66
|
+
memoryReviewPhysicalDestination(runRoot, relative);
|
|
129
67
|
fs.mkdirSync(root, { mode: 0o700 }); // Exclusive: never adopt an existing or partial destination.
|
|
130
|
-
|
|
68
|
+
memoryReviewPhysicalDestination(runRoot, relative);
|
|
131
69
|
fs.cpSync(run.workspace_root, root, { recursive: true, verbatimSymlinks: true, force: false, errorOnExist: true, filter: reviewSourceFilter(run.workspace_root, inputs, base) });
|
|
132
70
|
if (reviewSourceHash(run.workspace_root, inputs, base) !== intent.source_sha256
|
|
133
71
|
|| reviewExecutionCopyHash(root, run.workspace_root, false, inputs, base) !== intent.source_sha256)
|
|
@@ -9,6 +9,18 @@ import { prepareVnextSpecifyStart } from "./vnext-specify.js";
|
|
|
9
9
|
import { loadVnextWorkspacePolicy } from "./vnext-workspace-policy.js";
|
|
10
10
|
import { hybridDigest } from "./hybrid-contracts.js";
|
|
11
11
|
const identity = [".memory-bank/index.md", ".memory-bank/structure.md", ".memory-bank/project-policy.md"];
|
|
12
|
+
/** VERIFY diagnostics must remain reviewable when preparation fails. Neither
|
|
13
|
+
* outcome creates a future task or establishes provider access. */
|
|
14
|
+
export function observeMemoryNormalFlowEntryDiagnostic(context, projectRoot) {
|
|
15
|
+
try {
|
|
16
|
+
return { status: "prepared", observation: observeMemoryNormalFlowEntry(context, projectRoot) };
|
|
17
|
+
}
|
|
18
|
+
catch (error) {
|
|
19
|
+
if (!(error instanceof AppError))
|
|
20
|
+
throw error;
|
|
21
|
+
return { status: "unavailable", error: { code: error.code, reason: error.details?.reason ?? null } };
|
|
22
|
+
}
|
|
23
|
+
}
|
|
12
24
|
function refuse(reason) {
|
|
13
25
|
throw new AppError("memory_normal_flow_entry_invalid", "Ordinary SDLC entry cannot be prepared from initialized memory", 2, { reason, effect: "no_effect" });
|
|
14
26
|
}
|
|
@@ -28,7 +28,7 @@ function retainedUnbornBranch(context, workspace, scope) {
|
|
|
28
28
|
return policy.integration_branch;
|
|
29
29
|
}
|
|
30
30
|
/** Project-owned technical defaults only; never grants authority to a future task. */
|
|
31
|
-
export function prepareMemoryOrdinaryDefaults(context, workspace, selectedProfile, scope) {
|
|
31
|
+
export function prepareMemoryOrdinaryDefaults(context, workspace, selectedProfile, scope, integrationBranch, targetFeatureBranchTemplate) {
|
|
32
32
|
const proposals = [], missing = [];
|
|
33
33
|
const existing = ordinaryConfigurationPaths.map(logical => fs.existsSync(path.join(workspace, logical)));
|
|
34
34
|
if (existing[0])
|
|
@@ -61,14 +61,20 @@ export function prepareMemoryOrdinaryDefaults(context, workspace, selectedProfil
|
|
|
61
61
|
const remote = git(["symbolic-ref", "--quiet", "refs/remotes/origin/HEAD"]);
|
|
62
62
|
const branches = git(["for-each-ref", "--format=%(refname:short)", "refs/heads/"])?.split("\n").filter(Boolean) ?? [];
|
|
63
63
|
const unborn = !remote && !branches.length ? retainedUnbornBranch(context, workspace, scope) : null;
|
|
64
|
-
const
|
|
65
|
-
|
|
66
|
-
|
|
64
|
+
const remoteBranch = remote?.startsWith("refs/remotes/origin/") ? remote.slice("refs/remotes/origin/".length) : null;
|
|
65
|
+
// The Memory flow's retained explicit choice is also a technical default, not
|
|
66
|
+
// new merge/push authority. Existing ordinary configuration wins above.
|
|
67
|
+
const branch = integrationBranch ?? remoteBranch ?? (branches.length === 1 ? branches[0] : unborn);
|
|
68
|
+
reason = integrationBranch !== undefined ? "retained_explicit_integration_branch" : remoteBranch ? "verified_origin_head" : unborn ? "retained_unborn_init_branch_technical_default" : branch ? "verified_single_local_branch_technical_default" : "integration_branch_choice_required";
|
|
69
|
+
if (!branch || branch !== unborn && !git(["rev-parse", "--verify", `${integrationBranch === undefined && remoteBranch ? `refs/remotes/origin/${branch}` : `refs/heads/${branch}`}^{commit}`]))
|
|
67
70
|
missing.push("ordinary integration branch (multiple or unavailable candidates)");
|
|
68
71
|
else {
|
|
69
72
|
const contractFile = path.join(workspace, ".memory-bank/dd-flow/flow-contract.json");
|
|
70
73
|
const contract = fs.existsSync(contractFile) ? JSON.parse(fs.readFileSync(contractFile, "utf8")) : null;
|
|
71
|
-
const
|
|
74
|
+
const localTemplate = contract?.workspace?.feature_branch_template;
|
|
75
|
+
// Preserve compatible project routing; legacy PRT contracts use the
|
|
76
|
+
// retained upgrade target's default, not an unrelated local canon.
|
|
77
|
+
const template = typeof localTemplate === "string" && localTemplate.includes("<RUN>") ? localTemplate : targetFeatureBranchTemplate;
|
|
72
78
|
if (typeof template !== "string" || !template.includes("<RUN>"))
|
|
73
79
|
missing.push("ordinary feature branch template from selected flow contract");
|
|
74
80
|
else
|
|
@@ -110,3 +116,14 @@ export function assertMemoryOrdinaryConfigurationCoverage(value, defaults) {
|
|
|
110
116
|
throw new AppError("memory_stage_result_invalid", "Compose must assign the missing ordinary configuration to a reviewed unit and an exact write scope", 2, { phase: "prepare", effect: "no_effect", recoverable: true, reason: "ordinary_configuration_unassigned", path: logical });
|
|
111
117
|
}
|
|
112
118
|
}
|
|
119
|
+
/** Upgrade must admit missing project-owned paths before its diff is immutable. */
|
|
120
|
+
export function assertMemoryOrdinaryUpgradeConfigurationCoverage(value, defaults) {
|
|
121
|
+
const changes = value.details.path_map;
|
|
122
|
+
if (changes.some(change => change.action !== "retain" && [change.from, change.to].some(path => path !== null && defaults.preserved_paths.includes(path)))) {
|
|
123
|
+
throw new AppError("memory_stage_result_invalid", "Upgrade preserves valid existing ordinary configuration", 2, { phase: "prepare", effect: "no_effect", recoverable: true, reason: "existing_ordinary_configuration_write" });
|
|
124
|
+
}
|
|
125
|
+
for (const logical of defaults.required_paths)
|
|
126
|
+
if (!changes.some(change => change.from === null && change.to === logical && change.ownership === "project" && change.action === "add")) {
|
|
127
|
+
throw new AppError("memory_stage_result_invalid", "Upgrade analysis must assign each missing ordinary configuration as an exact project-owned addition", 2, { phase: "prepare", effect: "no_effect", recoverable: true, reason: "ordinary_configuration_unassigned", path: logical });
|
|
128
|
+
}
|
|
129
|
+
}
|
|
@@ -20,6 +20,7 @@ import { resolveProjectRoot } from "../storage/paths.js";
|
|
|
20
20
|
import { readAcceptedStageData } from "./stage-report-renderer.js";
|
|
21
21
|
import { readMemoryCompositionRegistration } from "./memory-composition-registration.js";
|
|
22
22
|
import { hybridDigest } from "./hybrid-contracts.js";
|
|
23
|
+
import { captureMemoryUpgradeSources } from "./memory-upgrade-analysis-handler.js";
|
|
23
24
|
function refuse(reason) {
|
|
24
25
|
throw new AppError("memory_preflight_handler_invalid", "Memory preflight cannot accept this execution or result", 2, { reason, effect: "no_effect" });
|
|
25
26
|
}
|
|
@@ -51,6 +52,20 @@ export function captureRegisteredMemoryCompositionSources(context, bundle, regis
|
|
|
51
52
|
const registration = readMemoryCompositionRegistration(context, registrationScope);
|
|
52
53
|
if (registration.run_id !== bundle.manifest.run_id || registration.execution_sha256 !== bundle.execution.sha256)
|
|
53
54
|
refuse("foreign_composition_registration");
|
|
55
|
+
if (bundle.descriptor.descriptor.id === "mb-upgrade") {
|
|
56
|
+
const blueprint = JSON.parse(Buffer.from(registration.result_base64, "base64").toString("utf8"));
|
|
57
|
+
const historical = new Map();
|
|
58
|
+
// Only restoring the independently registered blueprint. Its initial
|
|
59
|
+
// artifact references are not freshly produced Work or current outputs;
|
|
60
|
+
// final acceptance selects those independently from settled SQL results.
|
|
61
|
+
for (const unit of blueprint.units) {
|
|
62
|
+
for (const artifact of unit.artifacts)
|
|
63
|
+
historical.set(`${artifact.root}\0${artifact.path}`, artifact.sha256);
|
|
64
|
+
unit.artifacts = [];
|
|
65
|
+
}
|
|
66
|
+
return { sources: captureMemoryUpgradeSources(context, bundle, registrationScope.stage, Buffer.from(JSON.stringify(blueprint))),
|
|
67
|
+
historical, registrationSha256: hybridDigest(registration), inputSha256: registration.input_sha256 };
|
|
68
|
+
}
|
|
54
69
|
return { ...captureSources(context, bundle, Buffer.from(registration.result_base64, "base64"), true),
|
|
55
70
|
registrationSha256: hybridDigest(registration), inputSha256: registration.input_sha256 };
|
|
56
71
|
}
|
|
@@ -115,7 +115,7 @@ function compose(histories, paths, registration) {
|
|
|
115
115
|
for (const history of histories) {
|
|
116
116
|
if (!versions.has(hybridDigest(history.before().entries())))
|
|
117
117
|
refuse("unexplained_historical_input_view");
|
|
118
|
-
baseline = baseline.composeSettledDelta(history.before(), history.after());
|
|
118
|
+
baseline = baseline.composeSettledDelta(history.before(), history.after(), history.result().outcome);
|
|
119
119
|
versions.add(hybridDigest(baseline.entries()));
|
|
120
120
|
}
|
|
121
121
|
return baseline;
|