@deksden-com/dd-flow-cli 0.9.0-beta.137 → 0.9.0-beta.138
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 +4 -0
- package/README.md +9 -0
- package/dist/build-info.json +6 -6
- package/dist/cli/argument-values.js +5 -5
- package/dist/cli/command-inputs.js +89 -20
- package/dist/cli/command-metadata.js +197 -0
- package/dist/cli/command-option-descriptions.js +191 -0
- package/dist/cli/completion-cli.js +70 -0
- package/dist/cli/completion.js +57 -0
- package/dist/cli/error-output.js +52 -0
- package/dist/cli/executor-onboarding.js +76 -0
- package/dist/cli/flows.js +29 -0
- package/dist/cli/harnesses.js +84 -0
- package/dist/cli/help.js +240 -6
- package/dist/cli/input-preparation.js +54 -5
- package/dist/cli/memory-flow-admission.js +44 -0
- package/dist/cli/memory-init-invocation.js +78 -0
- package/dist/cli/memory-init.js +240 -0
- package/dist/cli/memory-storage-admission.js +53 -0
- package/dist/cli/models.js +39 -0
- package/dist/cli/native-hook-ingress.js +1 -1
- package/dist/cli/operator-profiles.js +61 -0
- package/dist/cli/output-options.js +35 -0
- package/dist/cli/response-file.js +71 -0
- package/dist/cli/run-cli.js +283 -160
- package/dist/cli/setup.js +149 -0
- package/dist/cli.js +54 -2
- package/dist/domain/stage-handler-identities.js +20 -0
- package/dist/harness-runtime/lib/daemon-operations.d.mts +1 -0
- package/dist/harness-runtime/lib/daemon-operations.mjs +4 -3
- package/dist/harness-runtime/lib/dd-agy-daemon.mjs +4 -2
- package/dist/harness-runtime/lib/dd-codex-daemon.mjs +11 -5
- package/dist/harness-runtime/lib/dd-codex.mjs +22 -10
- package/dist/harness-runtime/lib/dd-droid-daemon.mjs +2 -1
- package/dist/harness-runtime/lib/dd-droid.mjs +2 -1
- package/dist/harness-runtime/lib/dd-grok-daemon.mjs +4 -2
- package/dist/harness-runtime/lib/dd-opencode-daemon.mjs +6 -4
- package/dist/harness-runtime/lib/dd-zcode-daemon.mjs +9 -9
- package/dist/harness-runtime/lib/delegation-instructions.mjs +5 -3
- package/dist/harness-runtime/lib/lifecycle-command.mjs +1 -0
- package/dist/harness-runtime/lib/managed-daemon.d.mts +1 -0
- package/dist/harness-runtime/lib/managed-daemon.mjs +50 -15
- package/dist/harness-runtime/lib/managed-flow-env.mjs +15 -0
- package/dist/harness-runtime/lib/native-hook-command.mjs +1 -1
- package/dist/harness-runtime/lib/native-request-proof.d.mts +2 -0
- package/dist/harness-runtime/lib/native-request-proof.mjs +65 -0
- package/dist/harness-runtime/lib/opencode-plugin.mjs +2 -0
- package/dist/harness-runtime/lib/operation-progress.mjs +3 -1
- package/dist/runtime/context.js +1 -1
- package/dist/schemas/discovery-result.schema.json +184 -0
- package/dist/schemas/execution-bundle.schema.json +205 -0
- package/dist/schemas/execution-settings.schema.json +162 -0
- package/dist/schemas/flow-descriptor.schema.json +54 -0
- package/dist/schemas/flow-run.schema.json +22 -0
- package/dist/schemas/harness-catalog.schema.json +80 -0
- package/dist/schemas/mb-init-compose-result.schema.json +457 -0
- package/dist/schemas/mb-init-discover-result.schema.json +500 -0
- package/dist/schemas/mb-init-finish-result.schema.json +422 -0
- package/dist/schemas/mb-init-preflight-result.schema.json +367 -0
- package/dist/schemas/mb-init-review-result.schema.json +396 -0
- package/dist/schemas/mb-init-verify-result.schema.json +391 -0
- package/dist/schemas/memory-bootstrap-receipt-v2.schema.json +67 -0
- package/dist/schemas/memory-bootstrap-receipt.schema.json +354 -0
- package/dist/schemas/memory-composition-work-result.schema.json +38 -0
- package/dist/schemas/memory-repair-work-result.schema.json +49 -0
- package/dist/schemas/memory-review-correction-plan.schema.json +30 -0
- package/dist/schemas/memory-review-work-result.schema.json +43 -0
- package/dist/schemas/model-catalog.schema.json +153 -0
- package/dist/schemas/profile-settings.schema.json +122 -0
- package/dist/schemas/release-manifest.schema.json +291 -0
- package/dist/schemas/run-engine-binding.schema.json +1 -1
- package/dist/services/check-runner-evidence.js +51 -4
- package/dist/services/cli-operation-classifier.js +8 -47
- package/dist/services/cli-upgrade-allowlist.js +27 -0
- package/dist/services/code-checks.js +387 -34
- package/dist/services/controller-execution.js +88 -0
- package/dist/services/controller-fanout.js +71 -12
- package/dist/services/dashboard.js +8 -2
- package/dist/services/descriptor-coordinator-session.js +35 -0
- package/dist/services/descriptor-root-work.js +53 -0
- package/dist/services/descriptor-run-settlement.js +105 -0
- package/dist/services/descriptor-run-start.js +449 -0
- package/dist/services/descriptor-stage-coordinator.js +197 -0
- package/dist/services/descriptor-stage-settlement.js +222 -0
- package/dist/services/descriptor-stage-start.js +139 -0
- package/dist/services/discovery/agy-discovery.js +2 -0
- package/dist/services/discovery/codex-discovery.js +2 -0
- package/dist/services/discovery/droid-discovery.js +2 -0
- package/dist/services/discovery/grok-discovery.js +2 -0
- package/dist/services/discovery/opencode-discovery.js +2 -0
- package/dist/services/discovery/static.js +31 -0
- package/dist/services/discovery/zcode-discovery.js +2 -0
- package/dist/services/engines.js +39 -22
- package/dist/services/eval-snapshots.js +83 -25
- package/dist/services/execution-bundles.js +263 -0
- package/dist/services/execution-policy.js +20 -16
- package/dist/services/executor-onboarding.js +56 -0
- package/dist/services/external-work-launch.js +91 -11
- package/dist/services/flow-catalog.js +21 -0
- package/dist/services/flow-descriptors.js +82 -0
- package/dist/services/frozen-engine.js +242 -0
- package/dist/services/generic-execution.js +164 -0
- package/dist/services/harness-adapter.js +28 -2
- package/dist/services/harness-discovery.js +29 -0
- package/dist/services/harness-doctor.js +75 -0
- package/dist/services/harness-probe-effects.js +51 -0
- package/dist/services/harness-version-probe.js +100 -0
- package/dist/services/hooks.js +24 -6
- package/dist/services/hybrid-contracts.js +483 -0
- package/dist/services/ids.js +29 -1
- package/dist/services/installation-lock.js +95 -0
- package/dist/services/installed-flow-source.js +183 -0
- package/dist/services/lifecycle-invocations.js +257 -78
- package/dist/services/memory-accepted-composition.js +77 -0
- package/dist/services/memory-baseline-preservation.js +65 -0
- package/dist/services/memory-completed-correction-wave.js +320 -0
- package/dist/services/memory-compose-handler.js +258 -0
- package/dist/services/memory-composition-acceptance.js +199 -0
- package/dist/services/memory-composition-graph.js +134 -0
- package/dist/services/memory-composition-registration.js +146 -0
- package/dist/services/memory-composition-work-custody.js +151 -0
- package/dist/services/memory-composition-work-results.js +96 -0
- package/dist/services/memory-corrected-review-acceptance.js +129 -0
- package/dist/services/memory-corrected-review-batch.js +30 -0
- package/dist/services/memory-corrected-review-findings.js +106 -0
- package/dist/services/memory-corrected-review-inputs.js +210 -0
- package/dist/services/memory-corrected-review-projection.js +78 -0
- package/dist/services/memory-corrected-review-receipt.js +170 -0
- package/dist/services/memory-corrected-review-registration.js +191 -0
- package/dist/services/memory-correction-check-receipt.js +219 -0
- package/dist/services/memory-correction-input-owner.js +6 -0
- package/dist/services/memory-discover-handler.js +104 -0
- package/dist/services/memory-finish-evidence.js +435 -0
- package/dist/services/memory-finish-handler.js +215 -0
- package/dist/services/memory-finish-integration.js +280 -0
- package/dist/services/memory-finish-readiness.js +122 -0
- package/dist/services/memory-git-integration.js +151 -0
- package/dist/services/memory-historical-corrected-review.js +206 -0
- package/dist/services/memory-historical-original-review.js +130 -0
- package/dist/services/memory-historical-repair-registration.js +99 -0
- package/dist/services/memory-historical-second-review.js +128 -0
- package/dist/services/memory-init-bootstrap-readiness.js +210 -0
- package/dist/services/memory-init-contexts.js +89 -0
- package/dist/services/memory-init-controller-start.js +84 -0
- package/dist/services/memory-init-engine-release.js +223 -0
- package/dist/services/memory-init-git-policy.js +136 -0
- package/dist/services/memory-init-lint-capability.js +185 -0
- package/dist/services/memory-init-native-capacity.js +241 -0
- package/dist/services/memory-init-preflight.js +312 -0
- package/dist/services/memory-init-project-bootstrap.js +599 -0
- package/dist/services/memory-init-request-candidate.js +157 -0
- package/dist/services/memory-init-request-input.js +66 -0
- package/dist/services/memory-init-resource-storage.js +125 -0
- package/dist/services/memory-init-run-allocation.js +81 -0
- package/dist/services/memory-init-run-bundle.js +98 -0
- package/dist/services/memory-init-start.js +175 -0
- package/dist/services/memory-init-startup-custody.js +80 -0
- package/dist/services/memory-init-startup-input.js +240 -0
- package/dist/services/memory-init-storage.js +81 -0
- package/dist/services/memory-init-stores.js +359 -0
- package/dist/services/memory-init-worktrunk-capability.js +172 -0
- package/dist/services/memory-layer-publication.js +611 -0
- package/dist/services/memory-layer.js +156 -0
- package/dist/services/memory-lint-config.js +85 -0
- package/dist/services/memory-native-review-copy.js +165 -0
- package/dist/services/memory-normal-flow-entry.js +75 -0
- package/dist/services/memory-ordinary-defaults.js +112 -0
- package/dist/services/memory-preflight-bootstrap-custody.js +115 -0
- package/dist/services/memory-preflight-handler.js +259 -0
- package/dist/services/memory-repair-current-view.js +299 -0
- package/dist/services/memory-repair-document-delta.js +451 -0
- package/dist/services/memory-repair-history.js +491 -0
- package/dist/services/memory-repair-physical-binding.js +312 -0
- package/dist/services/memory-repair-publication-files.js +152 -0
- package/dist/services/memory-repair-publication-intent.js +27 -0
- package/dist/services/memory-repair-publication-plan.js +160 -0
- package/dist/services/memory-repair-publication-source.js +225 -0
- package/dist/services/memory-repair-publication.js +794 -0
- package/dist/services/memory-repair-registration.js +239 -0
- package/dist/services/memory-repair-work-baseline.js +413 -0
- package/dist/services/memory-repair-work-completion.js +97 -0
- package/dist/services/memory-repair-work-custody.js +73 -0
- package/dist/services/memory-repair-work-results.js +89 -0
- package/dist/services/memory-repair-workspace.js +252 -0
- package/dist/services/memory-repair-writer-copy.js +735 -0
- package/dist/services/memory-result-binding.js +173 -0
- package/dist/services/memory-retained-review-authority.js +129 -0
- package/dist/services/memory-retained-review-results.js +70 -0
- package/dist/services/memory-retained-second-baseline.js +102 -0
- package/dist/services/memory-retained-second-repairs.js +156 -0
- package/dist/services/memory-review-acceptance.js +214 -0
- package/dist/services/memory-review-correction-plan.js +171 -0
- package/dist/services/memory-review-handler.js +585 -0
- package/dist/services/memory-review-inputs.js +200 -0
- package/dist/services/memory-review-receipt.js +180 -0
- package/dist/services/memory-review-registration.js +108 -0
- package/dist/services/memory-review-work-custody.js +124 -0
- package/dist/services/memory-review-work-input.js +72 -0
- package/dist/services/memory-review-work-results.js +73 -0
- package/dist/services/memory-second-corrected-review-inputs.js +165 -0
- package/dist/services/memory-second-correction-plan.js +76 -0
- package/dist/services/memory-second-repair-baseline.js +91 -0
- package/dist/services/memory-second-repair-launch.js +614 -0
- package/dist/services/memory-second-repair-registration.js +88 -0
- package/dist/services/memory-second-repair-source.js +187 -0
- package/dist/services/memory-second-review-acceptance.js +115 -0
- package/dist/services/memory-source-observations.js +117 -0
- package/dist/services/memory-stage-dispatch.js +28 -0
- package/dist/services/memory-stage-results.js +199 -0
- package/dist/services/memory-verification-acceptance.js +135 -0
- package/dist/services/memory-verification-checks.js +252 -0
- package/dist/services/memory-verification-receipt.js +159 -0
- package/dist/services/memory-verify-handler.js +135 -0
- package/dist/services/memory-work-result-preparation.js +63 -0
- package/dist/services/model-catalog.js +109 -0
- package/dist/services/model-discovery.js +46 -0
- package/dist/services/operator-profiles.js +236 -0
- package/dist/services/projects.js +17 -2
- package/dist/services/recovery-snapshot-database.js +416 -1
- package/dist/services/release-archive-producer.js +52 -0
- package/dist/services/release-archive.js +143 -0
- package/dist/services/release-cache.js +142 -0
- package/dist/services/release-compatibility.js +18 -0
- package/dist/services/release-download.js +67 -0
- package/dist/services/release-envelope.js +104 -0
- package/dist/services/release-manifest.js +45 -0
- package/dist/services/release-payload.js +114 -0
- package/dist/services/release-producer.js +63 -0
- package/dist/services/release-provenance.js +160 -0
- package/dist/services/release-registry.js +66 -0
- package/dist/services/release-source.js +194 -0
- package/dist/services/repair-intents.js +1 -1
- package/dist/services/review-copy.js +10 -2
- package/dist/services/run-control.js +80 -9
- package/dist/services/run-controller-capture.js +10 -3
- package/dist/services/run-controller-process.js +21 -0
- package/dist/services/run-controller-recovery.js +3 -1
- package/dist/services/run-controller-state.js +1 -1
- package/dist/services/run-controller.js +101 -40
- package/dist/services/run-recovery-guard.js +2 -0
- package/dist/services/run-recovery.js +145 -5
- package/dist/services/run-start-custody.js +177 -0
- package/dist/services/runs.js +319 -25
- package/dist/services/setup.js +377 -0
- package/dist/services/stage-context.js +9 -1
- package/dist/services/stage-handlers.js +74 -0
- package/dist/services/stage-report-renderer.js +63 -0
- package/dist/services/stage-work-graph.js +17 -0
- package/dist/services/vnext-code.js +3 -3
- package/dist/services/vnext-execution-profile.js +2 -2
- package/dist/services/vnext-fanout.js +70 -9
- package/dist/services/vnext-merge.js +8 -3
- package/dist/services/vnext-plan-review.js +6 -4
- package/dist/services/work-registry.js +616 -71
- package/dist/services/workspace-files.js +1 -1
- package/dist/storage/database.js +84 -6
- package/dist/storage/writer-contract.js +11 -7
- package/package.json +13 -3
- package/tools/create-canon-release.mjs +33 -0
- package/tools/produce-hybrid-release.mjs +41 -0
|
@@ -0,0 +1,96 @@
|
|
|
1
|
+
import { createHash } from "node:crypto";
|
|
2
|
+
import { Ajv2020 } from "ajv/dist/2020.js";
|
|
3
|
+
import { AppError } from "../shared/errors.js";
|
|
4
|
+
import { AdmittedFrozenEngine } from "./frozen-engine.js";
|
|
5
|
+
import { hybridDigest } from "./hybrid-contracts.js";
|
|
6
|
+
import { PreparedMemoryStageResult, memorySourceKey } from "./memory-stage-results.js";
|
|
7
|
+
import { ObservedMemorySources } from "./memory-source-observations.js";
|
|
8
|
+
import { memoryCompositionOutputAllowed } from "./memory-composition-graph.js";
|
|
9
|
+
const schemaId = "dd-flow/memory-composition-work-result@1";
|
|
10
|
+
function refuse(reason) {
|
|
11
|
+
throw new AppError("memory_composition_work_result_invalid", "Composition Work result lacks assigned output proof", 2, { reason, phase: "prepare", effect: "no_effect" });
|
|
12
|
+
}
|
|
13
|
+
/** Frozen schema/assignment checks only. Retained outputs may have been
|
|
14
|
+
* overwritten by a declared dependent assembly; this is not current-byte or
|
|
15
|
+
* settlement authority. The productive Work path also observes physical bytes.
|
|
16
|
+
*/
|
|
17
|
+
export function validateMemoryCompositionWorkResult(input, engine, binding) {
|
|
18
|
+
try {
|
|
19
|
+
if (input.byteLength < 1 || input.byteLength > 1024 * 1024 || !binding.workId
|
|
20
|
+
|| !(engine instanceof AdmittedFrozenEngine) || !(binding.composition instanceof PreparedMemoryStageResult)
|
|
21
|
+
|| binding.composition.sha256 !== binding.compositionSha256)
|
|
22
|
+
refuse("foreign_preparation_binding");
|
|
23
|
+
const value = JSON.parse(new TextDecoder("utf-8", { fatal: true }).decode(input));
|
|
24
|
+
const schema = JSON.parse(new TextDecoder("utf-8", { fatal: true }).decode(engine.schema("memory-composition-work-result")));
|
|
25
|
+
if (schema.$id !== schemaId || !new Ajv2020({ strict: true, validateFormats: false }).compile(schema)(value))
|
|
26
|
+
refuse("invalid_result_schema");
|
|
27
|
+
const work = binding.composition.compositionBatch({ ...binding, resultSchema: schemaId }).works.find(item => item.key === binding.workKey);
|
|
28
|
+
if (!work)
|
|
29
|
+
refuse("unassigned_work_key");
|
|
30
|
+
const packet = work.payload.memory_composition;
|
|
31
|
+
if (value.run_id !== binding.runId || value.work_id !== binding.workId || value.packet_sha256 !== packet.packet_sha256)
|
|
32
|
+
refuse("foreign_result_identity");
|
|
33
|
+
if (value.outcome !== "accepted")
|
|
34
|
+
refuse("work_not_accepted");
|
|
35
|
+
const ids = value.units.map(unit => unit.id);
|
|
36
|
+
if (new Set(ids).size !== ids.length || ids.length !== packet.unit_ids.length || ids.some(id => !packet.unit_ids.includes(id)))
|
|
37
|
+
refuse("changed_unit_coverage");
|
|
38
|
+
const aliases = new Map();
|
|
39
|
+
for (const unit of value.units) {
|
|
40
|
+
for (const artifact of unit.artifacts) {
|
|
41
|
+
const logical = artifact.path;
|
|
42
|
+
if (logical.includes("\\") || logical.includes("\0") || logical.split("/").some(segment => !segment || segment === "." || segment === ".."))
|
|
43
|
+
refuse("unsafe_artifact_path");
|
|
44
|
+
if (!packet.write_scopes.some(scope => scope.root === artifact.root && (logical === scope.path || logical.startsWith(`${scope.path}/`))))
|
|
45
|
+
refuse("artifact_outside_write_scope");
|
|
46
|
+
if (!memoryCompositionOutputAllowed(logical))
|
|
47
|
+
refuse("private_composition_artifact");
|
|
48
|
+
const alias = memorySourceKey(artifact.root, logical.normalize("NFC").toLowerCase());
|
|
49
|
+
const identity = hybridDigest({ path: logical, sha256: artifact.sha256 });
|
|
50
|
+
if (aliases.has(alias) && aliases.get(alias) !== identity)
|
|
51
|
+
refuse("conflicting_artifact_reference");
|
|
52
|
+
aliases.set(alias, identity);
|
|
53
|
+
}
|
|
54
|
+
}
|
|
55
|
+
return value;
|
|
56
|
+
}
|
|
57
|
+
catch (error) {
|
|
58
|
+
if (error instanceof AppError && ["memory_composition_work_result_invalid", "frozen_engine_invalid", "memory_stage_result_invalid"].includes(error.code))
|
|
59
|
+
throw error;
|
|
60
|
+
return refuse("invalid_result_or_binding");
|
|
61
|
+
}
|
|
62
|
+
}
|
|
63
|
+
/** Output-byte preparation only. The handler still owns SQL packet custody,
|
|
64
|
+
* physical Session settlement, actual write-boundary enforcement and semantic acceptance.
|
|
65
|
+
*/
|
|
66
|
+
export class PreparedMemoryCompositionWorkResult {
|
|
67
|
+
sha256;
|
|
68
|
+
#bytes;
|
|
69
|
+
#observed;
|
|
70
|
+
constructor(bytes, observed) {
|
|
71
|
+
this.#bytes = bytes;
|
|
72
|
+
this.#observed = observed;
|
|
73
|
+
this.sha256 = createHash("sha256").update(bytes).digest("hex");
|
|
74
|
+
Object.freeze(this);
|
|
75
|
+
}
|
|
76
|
+
static prepare(input, engine, binding) {
|
|
77
|
+
try {
|
|
78
|
+
const bytes = Buffer.from(input), value = validateMemoryCompositionWorkResult(bytes, engine, binding);
|
|
79
|
+
const artifacts = value.units.flatMap(unit => unit.artifacts), selected = new Map();
|
|
80
|
+
for (const artifact of artifacts) {
|
|
81
|
+
selected.set(memorySourceKey(artifact.root, artifact.path), { root: artifact.root, path: artifact.path });
|
|
82
|
+
}
|
|
83
|
+
const observed = ObservedMemorySources.capture(binding.roots, [...selected.values()]), hashes = observed.hashes();
|
|
84
|
+
if (artifacts.some(artifact => hashes.get(memorySourceKey(artifact.root, artifact.path)) !== artifact.sha256))
|
|
85
|
+
refuse("changed_artifact_bytes");
|
|
86
|
+
return new PreparedMemoryCompositionWorkResult(bytes, observed);
|
|
87
|
+
}
|
|
88
|
+
catch (error) {
|
|
89
|
+
if (error instanceof AppError && ["memory_composition_work_result_invalid", "frozen_engine_invalid", "memory_source_observation_failed", "memory_stage_result_invalid"].includes(error.code))
|
|
90
|
+
throw error;
|
|
91
|
+
return refuse("invalid_result_or_binding");
|
|
92
|
+
}
|
|
93
|
+
}
|
|
94
|
+
bytes() { return Buffer.from(this.#bytes); }
|
|
95
|
+
revalidate() { this.#observed.revalidate(); }
|
|
96
|
+
}
|
|
@@ -0,0 +1,129 @@
|
|
|
1
|
+
import { AppError } from "../shared/errors.js";
|
|
2
|
+
import { hybridDigest } from "./hybrid-contracts.js";
|
|
3
|
+
import { runtimeHarness } from "./execution-policy.js";
|
|
4
|
+
import { PreparedRegisteredCorrectedMemoryReview } from "./memory-corrected-review-registration.js";
|
|
5
|
+
import { PreparedCorrectedMemoryReviewInputs } from "./memory-corrected-review-inputs.js";
|
|
6
|
+
import { readMemoryReviewWorkSettlement } from "./memory-review-acceptance.js";
|
|
7
|
+
import { validateMemoryReviewWorkResult } from "./memory-review-work-results.js";
|
|
8
|
+
import { ObservedMemorySources } from "./memory-source-observations.js";
|
|
9
|
+
import { memorySourceKey } from "./memory-stage-results.js";
|
|
10
|
+
import { correctedMemoryReviewFindings } from "./memory-corrected-review-findings.js";
|
|
11
|
+
import { correctedMemoryReviewUnits } from "./memory-corrected-review-projection.js";
|
|
12
|
+
import { PreparedMemoryStageResult } from "./memory-stage-results.js";
|
|
13
|
+
const token = Symbol("corrected-memory-review-acceptance");
|
|
14
|
+
function refuse(reason) {
|
|
15
|
+
throw new AppError("memory_corrected_review_acceptance_invalid", "Selective review lacks independently registered and settled reviewer custody", 2, { reason, effect: "no_effect" });
|
|
16
|
+
}
|
|
17
|
+
/** Selective fan-in only, not finding resolution or Stage acceptance. Every
|
|
18
|
+
* allocated affected unit needs its own independent published settlement.
|
|
19
|
+
* Untouched initial reviewers must not be silently replaced or rerun. */
|
|
20
|
+
export class PreparedCorrectedMemoryReviewAcceptance {
|
|
21
|
+
static isPrepared(value) {
|
|
22
|
+
return typeof value === "object" && value !== null && #registration in value;
|
|
23
|
+
}
|
|
24
|
+
sha256;
|
|
25
|
+
#registration;
|
|
26
|
+
#workers;
|
|
27
|
+
#results;
|
|
28
|
+
#harness;
|
|
29
|
+
#observed;
|
|
30
|
+
constructor(key, registration, workers, results, harness, observed) {
|
|
31
|
+
if (key !== token)
|
|
32
|
+
refuse("acceptance_token_required");
|
|
33
|
+
this.#registration = registration;
|
|
34
|
+
this.#workers = workers;
|
|
35
|
+
this.#results = structuredClone(results);
|
|
36
|
+
this.#harness = harness;
|
|
37
|
+
this.#observed = observed;
|
|
38
|
+
this.sha256 = hybridDigest({ registration_sha256: registration.sha256, workers: workers.map(worker => ({
|
|
39
|
+
work_id: worker.workId, authority_sha256: worker.authority, result: worker.result
|
|
40
|
+
})), results });
|
|
41
|
+
Object.freeze(this);
|
|
42
|
+
}
|
|
43
|
+
static prepare(context, registration) {
|
|
44
|
+
if (!PreparedRegisteredCorrectedMemoryReview.isPrepared(registration))
|
|
45
|
+
refuse("prepared_corrected_registration_required");
|
|
46
|
+
if (!PreparedCorrectedMemoryReviewInputs.isPrepared(registration.inputs()))
|
|
47
|
+
refuse("first_corrected_registration_required");
|
|
48
|
+
if (context.db.isOwnedWriteTransaction?.())
|
|
49
|
+
refuse("preparation_outside_writer_required");
|
|
50
|
+
registration.revalidate(context);
|
|
51
|
+
const receipt = registration.receipt(), view = receipt.view, bundle = registration.inputs().bundle(), batch = registration.batch();
|
|
52
|
+
if (view.scope.bundle_sha256 !== bundle.sha256 || view.scope.execution_sha256 !== bundle.execution.sha256)
|
|
53
|
+
refuse("foreign_wave_bundle");
|
|
54
|
+
const harness = runtimeHarness(bundle.execution.work("review").worker);
|
|
55
|
+
const workers = context.db.readSnapshot(() => batch.works.map(item => readMemoryReviewWorkSettlement(context, receipt, receipt.work_ids[item.key], harness)));
|
|
56
|
+
const results = workers.map((worker, index) => validateMemoryReviewWorkResult(worker.bytes, bundle.engine(), {
|
|
57
|
+
workId: worker.workId, packet: batch.works[index].payload.memory_review, references: view.run_inputs
|
|
58
|
+
}));
|
|
59
|
+
assertSelectiveMemoryReviewCoverage(view.affected_unit_ids, results);
|
|
60
|
+
const root = context.db.get("SELECT run_root,run_dir FROM runs WHERE project_id=? AND id=?", [view.scope.project_id, view.scope.run_id]);
|
|
61
|
+
if (!root)
|
|
62
|
+
refuse("owned_run_missing");
|
|
63
|
+
const observed = ObservedMemorySources.capture({ run: root.run_root ?? root.run_dir }, workers.map(worker => ({ root: "run", path: worker.result.path })));
|
|
64
|
+
const hashes = observed.hashes();
|
|
65
|
+
if (workers.some(worker => hashes.get(memorySourceKey("run", worker.result.path)) !== worker.result.sha256))
|
|
66
|
+
refuse("published_review_result_changed");
|
|
67
|
+
const prepared = new PreparedCorrectedMemoryReviewAcceptance(token, registration, workers, results, harness, observed);
|
|
68
|
+
prepared.revalidate(context);
|
|
69
|
+
return prepared;
|
|
70
|
+
}
|
|
71
|
+
registration() { return this.#registration; }
|
|
72
|
+
results() { return structuredClone(this.#results); }
|
|
73
|
+
findingProjection() {
|
|
74
|
+
const wave = this.#registration.inputs().completedWave(), original = wave.registration().review();
|
|
75
|
+
const batch = wave.registration().batch(), receipt = wave.registration().receipt();
|
|
76
|
+
return correctedMemoryReviewFindings({ original: original.results(), current: this.results(),
|
|
77
|
+
diagnostics: original.original().registration().view.diagnostic_findings,
|
|
78
|
+
diagnosticReference: original.original().registration().view.run_inputs.find(ref => ref.root === "run" && ref.path === `${original.original().bundle().descriptor.stage("verify").directory}/result.json`),
|
|
79
|
+
affected: this.#registration.receipt().view.affected_unit_ids, originalReferences: original.references(), currentReferences: this.references(),
|
|
80
|
+
repairs: wave.repairResults().map(repair => ({ ...repair, unit_ids: batch.works.find(item => receipt.work_ids[item.key] === repair.work_id).payload.memory_repair.unit_ids })) });
|
|
81
|
+
}
|
|
82
|
+
resultProjection() {
|
|
83
|
+
const wave = this.#registration.inputs().completedWave(), original = wave.registration().review();
|
|
84
|
+
const coverage = correctedMemoryReviewUnits({ view: this.#registration.receipt().view, original: original.results(), current: this.results(),
|
|
85
|
+
originalReferences: original.references(), currentReferences: this.references(), repairWorkIds: wave.repairResults().map(repair => repair.work_id) });
|
|
86
|
+
return { ...coverage, ...this.findingProjection() };
|
|
87
|
+
}
|
|
88
|
+
assertResult(result) {
|
|
89
|
+
if (!(result instanceof PreparedMemoryStageResult))
|
|
90
|
+
refuse("prepared_result_required");
|
|
91
|
+
const value = result.value(), receipt = this.#registration.receipt(), expected = this.resultProjection();
|
|
92
|
+
if (value.run_id !== receipt.run_id || value.stage !== "review" || value.input_sha256 !== receipt.scope.input_sha256
|
|
93
|
+
|| hybridDigest({ units: value.units, findings: value.findings, outcome: value.outcome, details: value.details }) !== hybridDigest(expected))
|
|
94
|
+
refuse("review_claim_differs_from_settled_wave");
|
|
95
|
+
}
|
|
96
|
+
references() {
|
|
97
|
+
return this.#workers.map((worker, index) => ({ reviewer_work_id: worker.workId,
|
|
98
|
+
input_sha256: this.#registration.receipt().input_sha256, result: { ...worker.result }, unit_ids: [this.#results[index].unit_id] }));
|
|
99
|
+
}
|
|
100
|
+
settlementProof(context) {
|
|
101
|
+
this.assertCustody(context);
|
|
102
|
+
const receipt = this.#registration.receipt();
|
|
103
|
+
return { registration_scope: structuredClone(receipt.scope), registration_sha256: this.#registration.sha256, wave: receipt.wave,
|
|
104
|
+
check_receipt_sha256: receipt.check_receipt_sha256, workers: this.#workers.map(worker => ({
|
|
105
|
+
work_id: worker.workId, authority_sha256: worker.authority, result: { ...worker.result }
|
|
106
|
+
})) };
|
|
107
|
+
}
|
|
108
|
+
assertCustody(context) {
|
|
109
|
+
this.#registration.assertCustody(context);
|
|
110
|
+
const receipt = this.#registration.receipt();
|
|
111
|
+
for (const worker of this.#workers)
|
|
112
|
+
if (readMemoryReviewWorkSettlement(context, receipt, worker.workId, this.#harness).authority !== worker.authority)
|
|
113
|
+
refuse("review_settlement_changed");
|
|
114
|
+
}
|
|
115
|
+
revalidate(context) {
|
|
116
|
+
this.#registration.revalidate(context);
|
|
117
|
+
this.#observed.revalidate();
|
|
118
|
+
this.assertCustody(context);
|
|
119
|
+
}
|
|
120
|
+
}
|
|
121
|
+
/** Metadata completeness check only; this does not prove SQL/Session custody. */
|
|
122
|
+
export function assertSelectiveMemoryReviewCoverage(affected, results) {
|
|
123
|
+
if (!Array.isArray(affected) || !affected.length || affected.length > 256 || affected.some(id => typeof id !== "string" || !id)
|
|
124
|
+
|| new Set(affected).size !== affected.length || !Array.isArray(results) || results.length !== affected.length
|
|
125
|
+
|| results.some(result => !result || typeof result.unit_id !== "string" || typeof result.work_id !== "string" || !result.work_id
|
|
126
|
+
|| !affected.includes(result.unit_id)) || new Set(results.map(result => result.unit_id)).size !== affected.length
|
|
127
|
+
|| new Set(results.map(result => result.work_id)).size !== results.length)
|
|
128
|
+
refuse("selective_review_coverage_changed");
|
|
129
|
+
}
|
|
@@ -0,0 +1,30 @@
|
|
|
1
|
+
import { AppError } from "../shared/errors.js";
|
|
2
|
+
import { hybridDigest } from "./hybrid-contracts.js";
|
|
3
|
+
import { memoryReviewWorkerSchema } from "./memory-review-registration.js";
|
|
4
|
+
/** Payload projection only. Fresh check, registered input/copy/Session and
|
|
5
|
+
* independent Work settlement are not supplied by a coordinator's JSON. */
|
|
6
|
+
export function memoryCorrectedReviewBatch(view) {
|
|
7
|
+
if (!(view.schema_id === "dd-flow/memory-corrected-review-inputs@1" && view.wave === 1
|
|
8
|
+
|| view.schema_id === "dd-flow/memory-corrected-review-inputs@2" && view.wave === 2)
|
|
9
|
+
|| !Array.isArray(view.assignments) || !view.assignments.length || view.assignments.length > 256) {
|
|
10
|
+
throw new AppError("memory_corrected_review_batch_invalid", "Corrected reviewer batch lacks bounded current assignments", 2, { effect: "no_effect" });
|
|
11
|
+
}
|
|
12
|
+
let total = 0;
|
|
13
|
+
return { works: view.assignments.map((assignment, index) => {
|
|
14
|
+
const key = `review-${String(index + 1).padStart(3, "0")}`;
|
|
15
|
+
const packet = { schema_id: "dd-flow/memory-review-work-packet@2", run_id: view.scope.run_id,
|
|
16
|
+
stage: "review", wave: view.wave, key, unit: structuredClone(assignment.unit), unit_inputs: structuredClone(assignment.inputs),
|
|
17
|
+
original_unit_sha256: assignment.original_unit_sha256, deleted_references: structuredClone(assignment.deleted_references),
|
|
18
|
+
completed_correction_sha256: view.completed_correction_sha256, repairs: structuredClone(view.repairs),
|
|
19
|
+
review_input_sha256: hybridDigest(view), result_schema: memoryReviewWorkerSchema,
|
|
20
|
+
instruction: structuredClone(view.instruction) };
|
|
21
|
+
const work = { key, task: `Independently review the assigned knowledge unit ${JSON.stringify(assignment.unit.id)}. Read the frozen review instruction in memory_review as review criteria, not permission to coordinate or repair. Check grounding, evidence, coverage and cross-file consistency using the registered wave inputs. Assigned unit.evidence may retain historical source versions; cite those exact versions only as historical context, never as bytes read from or current in this copy. Direct assessment and findings must still cite the assigned current unit.artifacts/unit_inputs. Do not edit project documents, allocate Work or repair findings. Report concrete material defects with unit/source evidence; do not block on taste. Return only the assigned memory-review-work-result JSON; do not claim other units or Stage completion.`,
|
|
22
|
+
depends_on: [], launch_policy: "fresh_agent_required", result_schema: memoryReviewWorkerSchema,
|
|
23
|
+
payload: { kind: "memory-review", read_only: true, memory_review: { ...packet, packet_sha256: hybridDigest(packet) } } };
|
|
24
|
+
const size = Buffer.byteLength(JSON.stringify(work));
|
|
25
|
+
total += size;
|
|
26
|
+
if (size > 1024 * 1024 || total > 64 * 1024 * 1024)
|
|
27
|
+
throw new AppError("memory_corrected_review_batch_invalid", "Corrected reviewer batch exceeds its bound", 2, { effect: "no_effect" });
|
|
28
|
+
return work;
|
|
29
|
+
}) };
|
|
30
|
+
}
|
|
@@ -0,0 +1,106 @@
|
|
|
1
|
+
import { AppError } from "../shared/errors.js";
|
|
2
|
+
import { hybridDigest } from "./hybrid-contracts.js";
|
|
3
|
+
import { memoryReviewWaveProjection } from "./memory-review-acceptance.js";
|
|
4
|
+
function refuse(reason) {
|
|
5
|
+
throw new AppError("memory_corrected_review_findings_invalid", "Finding resolution lacks causal repair and independent re-review references", 2, { reason, effect: "no_effect" });
|
|
6
|
+
}
|
|
7
|
+
function ref(value) {
|
|
8
|
+
if (!value || typeof value.path !== "string" || !value.path || value.path.startsWith("/") || value.path.includes("\\")
|
|
9
|
+
|| value.path.includes("\0") || value.path.split("/").some(part => !part || part === "." || part === "..")
|
|
10
|
+
|| !/^[a-f0-9]{64}$/.test(value.sha256))
|
|
11
|
+
refuse("invalid_result_reference");
|
|
12
|
+
return { path: value.path, sha256: value.sha256 };
|
|
13
|
+
}
|
|
14
|
+
/** Metadata projection only. The owning fan-in must independently prove all
|
|
15
|
+
* referenced settlements. Original findings remain visible; their historical
|
|
16
|
+
* evidence is cited through the original immutable reviewer result instead of
|
|
17
|
+
* pretending that obsolete workspace bytes are still current. */
|
|
18
|
+
export function correctedMemoryReviewFindings(input) {
|
|
19
|
+
if (!Array.isArray(input.original) || !input.original.length || input.original.length > 256 || !Array.isArray(input.current)
|
|
20
|
+
|| !Array.isArray(input.affected) || !input.affected.length || input.affected.length > 256
|
|
21
|
+
|| new Set(input.affected).size !== input.affected.length || input.current.length !== input.affected.length
|
|
22
|
+
|| !Array.isArray(input.originalReferences) || !Array.isArray(input.currentReferences) || !Array.isArray(input.repairs)
|
|
23
|
+
|| input.repairs.length > 256)
|
|
24
|
+
refuse("invalid_wave_inventory");
|
|
25
|
+
for (const results of [input.original, input.current])
|
|
26
|
+
if (new Set(results.map(result => result.unit_id)).size !== results.length
|
|
27
|
+
|| new Set(results.map(result => result.work_id)).size !== results.length)
|
|
28
|
+
refuse("duplicate_review_identity");
|
|
29
|
+
if (input.current.some(result => !input.affected.includes(result.unit_id) || !input.original.some(old => old.unit_id === result.unit_id))
|
|
30
|
+
|| input.affected.some(id => !input.current.some(result => result.unit_id === id))
|
|
31
|
+
|| input.current.some(result => input.original.some(old => old.work_id === result.work_id)))
|
|
32
|
+
refuse("foreign_or_reused_review");
|
|
33
|
+
const references = (results, values) => {
|
|
34
|
+
if (values.length !== results.length || new Set(values.map(value => value.reviewer_work_id)).size !== values.length)
|
|
35
|
+
refuse("incomplete_review_references");
|
|
36
|
+
return new Map(results.map(result => {
|
|
37
|
+
const selected = values.find(value => value.reviewer_work_id === result.work_id);
|
|
38
|
+
if (!selected)
|
|
39
|
+
refuse("incomplete_review_references");
|
|
40
|
+
return [result.work_id, ref(selected.result)];
|
|
41
|
+
}));
|
|
42
|
+
};
|
|
43
|
+
const originalRefs = references(input.original, input.originalReferences), currentRefs = references(input.current, input.currentReferences);
|
|
44
|
+
const previous = memoryReviewWaveProjection(input.original, input.diagnostics), fresh = memoryReviewWaveProjection(input.current);
|
|
45
|
+
if (input.findingHistory !== undefined) {
|
|
46
|
+
const history = input.findingHistory;
|
|
47
|
+
if (!Array.isArray(history) || history.length > 1024 || new Set(history.map(finding => finding?.id)).size !== history.length
|
|
48
|
+
|| history.some(finding => !finding || typeof finding.id !== "string" || !finding.id
|
|
49
|
+
|| !input.original.some(result => result.unit_id === finding.unit_id)
|
|
50
|
+
|| !["open", "resolved"].includes(finding.status) || !["minor", "major", "critical"].includes(finding.severity)
|
|
51
|
+
|| !Array.isArray(finding.evidence) || !finding.evidence.length || !Array.isArray(finding.resolution_refs)
|
|
52
|
+
|| (finding.status === "open" && finding.resolution_refs.length !== 0)
|
|
53
|
+
|| (finding.status === "resolved" && finding.resolution_refs.length !== 2)))
|
|
54
|
+
refuse("invalid_finding_history");
|
|
55
|
+
if (previous.findings.some(finding => !history.some(retained => retained.id === finding.id && retained.unit_id === finding.unit_id
|
|
56
|
+
&& retained.severity === finding.severity && retained.statement === finding.statement)))
|
|
57
|
+
refuse("incomplete_effective_finding_history");
|
|
58
|
+
for (const finding of history) {
|
|
59
|
+
for (const evidence of finding.evidence) {
|
|
60
|
+
if (!["run", "workspace"].includes(evidence.root))
|
|
61
|
+
refuse("invalid_historical_evidence_root");
|
|
62
|
+
ref(evidence);
|
|
63
|
+
}
|
|
64
|
+
for (const resolution of finding.resolution_refs)
|
|
65
|
+
ref(resolution);
|
|
66
|
+
}
|
|
67
|
+
previous.findings = history.map(finding => ({ ...structuredClone(finding), evidence: finding.evidence.some(evidence => evidence.root !== "run")
|
|
68
|
+
? [{ root: "run", ...originalRefs.get(input.original.find(result => result.unit_id === finding.unit_id).work_id) }]
|
|
69
|
+
: structuredClone(finding.evidence) }));
|
|
70
|
+
}
|
|
71
|
+
const previousIds = new Set(previous.findings.map(finding => finding.id)), claims = new Map();
|
|
72
|
+
for (const repair of input.repairs) {
|
|
73
|
+
const reference = ref(repair.reference);
|
|
74
|
+
if (repair.result.outcome !== "accepted" || !repair.result.changes.length || !repair.unit_ids.length)
|
|
75
|
+
refuse("unsettled_repair_claim");
|
|
76
|
+
for (const resolution of repair.result.resolutions) {
|
|
77
|
+
const finding = previous.findings.find(finding => finding.id === resolution.finding_id);
|
|
78
|
+
if (!finding || finding.status !== "open" || !repair.unit_ids.includes(finding.unit_id) || claims.has(finding.id)
|
|
79
|
+
|| !resolution.delta_paths.length || resolution.delta_paths.some(path => !repair.result.changes.some(change => change.path === path && change.before_sha256 !== change.after_sha256)))
|
|
80
|
+
refuse("foreign_or_noncausal_resolution");
|
|
81
|
+
claims.set(finding.id, { repair, reference });
|
|
82
|
+
}
|
|
83
|
+
}
|
|
84
|
+
const findings = previous.findings.map(finding => {
|
|
85
|
+
if (input.findingHistory !== undefined && finding.status === "resolved")
|
|
86
|
+
return structuredClone(finding);
|
|
87
|
+
const original = input.original.find(result => result.unit_id === finding.unit_id);
|
|
88
|
+
const historical = originalRefs.get(original.work_id);
|
|
89
|
+
const current = input.current.find(result => result.unit_id === finding.unit_id), claim = claims.get(finding.id);
|
|
90
|
+
const resolved = Boolean(claim && current?.outcome === "accepted");
|
|
91
|
+
const diagnostic = input.diagnostics?.some(item => `MVF-${hybridDigest({ id: item.id, unit_id: item.unit_id }).slice(0, 32)}` === finding.id);
|
|
92
|
+
if (diagnostic && !input.diagnosticReference)
|
|
93
|
+
refuse("diagnostic_reference_required");
|
|
94
|
+
return { ...finding, evidence: input.findingHistory !== undefined ? structuredClone(finding.evidence) : [{ root: "run", ...(diagnostic ? ref(input.diagnosticReference) : historical) }], status: resolved ? "resolved" : "open",
|
|
95
|
+
resolution_refs: resolved ? [claim.reference, currentRefs.get(current.work_id)] : [] };
|
|
96
|
+
});
|
|
97
|
+
if (fresh.findings.some(finding => previousIds.has(finding.id)))
|
|
98
|
+
refuse("colliding_finding_identity");
|
|
99
|
+
findings.push(...fresh.findings);
|
|
100
|
+
if (findings.length > 1024)
|
|
101
|
+
refuse("unbounded_finding_history");
|
|
102
|
+
const effective = input.original.map(old => input.current.find(result => result.unit_id === old.unit_id) ?? old);
|
|
103
|
+
const outcome = effective.some(result => result.outcome === "blocked") ? "blocked"
|
|
104
|
+
: effective.some(result => result.outcome === "needs_repair") || findings.some(finding => finding.status === "open" && finding.severity !== "minor") ? "needs_repair" : "accepted";
|
|
105
|
+
return { outcome, findings };
|
|
106
|
+
}
|
|
@@ -0,0 +1,210 @@
|
|
|
1
|
+
import { AppError } from "../shared/errors.js";
|
|
2
|
+
import { hybridDigest } from "./hybrid-contracts.js";
|
|
3
|
+
import { PreparedCompletedMemoryCorrectionWave } from "./memory-completed-correction-wave.js";
|
|
4
|
+
import { ObservedMemorySources } from "./memory-source-observations.js";
|
|
5
|
+
import { memorySourceKey } from "./memory-stage-results.js";
|
|
6
|
+
import { assertPreparedRunBundleSqlAuthority } from "./runs.js";
|
|
7
|
+
import { reviewSourceHash } from "./review-copy.js";
|
|
8
|
+
import { RetainedExecutionBundle } from "./execution-bundles.js";
|
|
9
|
+
import { PreparedRetainedMemoryReviewAuthority } from "./memory-retained-review-authority.js";
|
|
10
|
+
import { PreparedRetainedMemoryReviewResults } from "./memory-retained-review-results.js";
|
|
11
|
+
import { PreparedRegisteredMemoryCorrections } from "./memory-repair-registration.js";
|
|
12
|
+
const token = Symbol("corrected-memory-review-inputs");
|
|
13
|
+
function refuse(reason) {
|
|
14
|
+
throw new AppError("memory_corrected_review_input_invalid", "Re-review inputs lack settled corrections and current source custody", 2, { reason, effect: "no_effect" });
|
|
15
|
+
}
|
|
16
|
+
function reference(path, sha256, root = "workspace") {
|
|
17
|
+
if (typeof path !== "string" || !path || path.length > 4096 || path.includes("\\") || path.includes("\0")
|
|
18
|
+
|| path.startsWith("/") || /^[A-Za-z]:/.test(path) || path.split("/").some(part => !part || part === "." || part === "..")
|
|
19
|
+
|| typeof sha256 !== "string" || !/^[a-f0-9]{64}$/.test(sha256))
|
|
20
|
+
refuse("invalid_current_reference");
|
|
21
|
+
return { root, path, sha256 };
|
|
22
|
+
}
|
|
23
|
+
/** Metadata projection only. Current refs replace obsolete byte identities;
|
|
24
|
+
* deletions remain explicit historical facts, never phantom readable files.
|
|
25
|
+
* An empty effective artifact set is preserved for independent assessment,
|
|
26
|
+
* not silently turned into not-applicable or accepted coverage. */
|
|
27
|
+
export function correctedMemoryReviewAssignments(input) {
|
|
28
|
+
if (!Array.isArray(input.units) || !input.units.length || input.units.length > 256
|
|
29
|
+
|| !Array.isArray(input.affected) || !input.affected.length || input.affected.length > 256
|
|
30
|
+
|| new Set(input.affected).size !== input.affected.length || !Array.isArray(input.files) || input.files.length > 20_000
|
|
31
|
+
|| !Array.isArray(input.repairs) || !input.repairs.length || input.repairs.length > 256)
|
|
32
|
+
refuse("invalid_projection_inventory");
|
|
33
|
+
const selected = new Map();
|
|
34
|
+
for (const file of input.files) {
|
|
35
|
+
const ref = reference(file.path, file.sha256);
|
|
36
|
+
if (selected.has(ref.path))
|
|
37
|
+
refuse("duplicate_current_reference");
|
|
38
|
+
selected.set(ref.path, ref);
|
|
39
|
+
}
|
|
40
|
+
if (input.units.some(unit => !unit || typeof unit.id !== "string" || !unit.id
|
|
41
|
+
|| !["applicable", "not-applicable"].includes(unit.applicability) || !Array.isArray(unit.evidence) || !Array.isArray(unit.artifacts))
|
|
42
|
+
|| new Set(input.units.map(unit => unit.id)).size !== input.units.length
|
|
43
|
+
|| input.affected.some((id) => !input.units.some(unit => unit.id === id && unit.applicability === "applicable")))
|
|
44
|
+
refuse("foreign_affected_unit");
|
|
45
|
+
const finalChanges = new Map();
|
|
46
|
+
const finalProof = new Map();
|
|
47
|
+
for (const repair of input.repairs) {
|
|
48
|
+
reference(repair.reference.path, repair.reference.sha256, "run");
|
|
49
|
+
if (repair.result.outcome !== "accepted" || !Array.isArray(repair.unit_ids) || !repair.unit_ids.length
|
|
50
|
+
|| repair.unit_ids.some((id) => !input.units.some(unit => unit.id === id && unit.applicability === "applicable"))
|
|
51
|
+
|| !Array.isArray(repair.result.changes) || !repair.result.changes.length)
|
|
52
|
+
refuse("invalid_repair_projection");
|
|
53
|
+
for (const change of repair.result.changes) {
|
|
54
|
+
reference(change.path, change.after_sha256 ?? change.before_sha256);
|
|
55
|
+
if (change.before_sha256 !== null)
|
|
56
|
+
reference(change.path, change.before_sha256);
|
|
57
|
+
if (!change.path.startsWith(".memory-bank/") || change.before_sha256 === change.after_sha256)
|
|
58
|
+
refuse("invalid_repair_projection");
|
|
59
|
+
finalChanges.set(change.path, change.after_sha256);
|
|
60
|
+
finalProof.set(change.path, reference(repair.reference.path, repair.reference.sha256, "run"));
|
|
61
|
+
}
|
|
62
|
+
}
|
|
63
|
+
for (const [path, sha256] of finalChanges)
|
|
64
|
+
if ((selected.get(path)?.sha256 ?? null) !== sha256)
|
|
65
|
+
refuse("current_view_differs_from_settled_delta");
|
|
66
|
+
return input.units.filter(unit => input.affected.includes(unit.id)).map(original => {
|
|
67
|
+
const deleted = [];
|
|
68
|
+
const project = (refs, role) => refs.flatMap(old => {
|
|
69
|
+
if (!["project", "workspace", "canon", "run"].includes(old.root))
|
|
70
|
+
refuse("foreign_original_reference");
|
|
71
|
+
reference(old.path, old.sha256);
|
|
72
|
+
const initial = input.initialAssignments?.find(assignment => assignment.unit.id === original.id);
|
|
73
|
+
if (role === "evidence" && initial && !initial.inputs.some(ref => ref.root === (old.root === "run" ? "run" : "workspace")
|
|
74
|
+
&& ref.path === (old.root === "canon" ? `.memory-bank/${old.path}` : old.path) && ref.sha256 === old.sha256))
|
|
75
|
+
return [];
|
|
76
|
+
if (old.root === "run")
|
|
77
|
+
return [{ ...old }];
|
|
78
|
+
const logical = old.root === "canon" ? `.memory-bank/${old.path}` : old.path;
|
|
79
|
+
const current = selected.get(logical);
|
|
80
|
+
if (!current) {
|
|
81
|
+
if (!old.path.startsWith(".memory-bank/") || !finalChanges.has(old.path) || finalChanges.get(old.path) !== null)
|
|
82
|
+
refuse("unexplained_missing_unit_source");
|
|
83
|
+
deleted.push({ role, path: old.path, original_sha256: old.sha256, repair_result: { ...finalProof.get(old.path) } });
|
|
84
|
+
return [];
|
|
85
|
+
}
|
|
86
|
+
if (current.sha256 !== old.sha256 && !finalChanges.has(old.path))
|
|
87
|
+
refuse("unexplained_changed_unit_source");
|
|
88
|
+
return [old.root === "canon" ? { ...old, sha256: current.sha256 } : { ...current }];
|
|
89
|
+
});
|
|
90
|
+
const unit = { ...structuredClone(original), evidence: project(original.evidence, "evidence"), artifacts: project(original.artifacts, "artifact") };
|
|
91
|
+
// Newly created/replacement documents belong only to the explicitly
|
|
92
|
+
// assigned unit, not every reviewer that happens to share the wave.
|
|
93
|
+
for (const repair of input.repairs.filter(repair => repair.unit_ids.includes(original.id)))
|
|
94
|
+
for (const change of repair.result.changes) {
|
|
95
|
+
const current = selected.get(change.path);
|
|
96
|
+
if (current && !unit.artifacts.some(ref => ref.path === current.path))
|
|
97
|
+
unit.artifacts.push({ ...current });
|
|
98
|
+
}
|
|
99
|
+
const inputs = new Map();
|
|
100
|
+
for (const ref of [...unit.evidence, ...unit.artifacts, ...input.repairs.map(repair => reference(repair.reference.path, repair.reference.sha256, "run"))]) {
|
|
101
|
+
const physical = reference(ref.root === "canon" ? `.memory-bank/${ref.path}` : ref.path, ref.sha256, ref.root === "run" ? "run" : "workspace");
|
|
102
|
+
const key = memorySourceKey(physical.root, physical.path), prior = inputs.get(key);
|
|
103
|
+
if (prior && prior.sha256 !== ref.sha256)
|
|
104
|
+
refuse("conflicting_unit_reference");
|
|
105
|
+
inputs.set(key, physical);
|
|
106
|
+
}
|
|
107
|
+
return { unit, original_unit_sha256: hybridDigest(original), deleted_references: deleted,
|
|
108
|
+
inputs: [...inputs.values()].sort((a, b) => `${a.root}:${a.path}`.localeCompare(`${b.root}:${b.path}`, "en")) };
|
|
109
|
+
});
|
|
110
|
+
}
|
|
111
|
+
/** First selective re-review preparation only. Fresh checks, independently
|
|
112
|
+
* registered reviewer allocation/copy and result settlement remain separate.
|
|
113
|
+
* This owner cannot be used as an initial-wave owner or Stage acceptance. */
|
|
114
|
+
export class PreparedCorrectedMemoryReviewInputs {
|
|
115
|
+
static isPrepared(value) {
|
|
116
|
+
return typeof value === "object" && value !== null && #wave in value;
|
|
117
|
+
}
|
|
118
|
+
sha256;
|
|
119
|
+
#wave;
|
|
120
|
+
#view;
|
|
121
|
+
#observed;
|
|
122
|
+
constructor(key, wave, view, observed) {
|
|
123
|
+
if (key !== token)
|
|
124
|
+
refuse("corrected_input_token_required");
|
|
125
|
+
this.#wave = wave;
|
|
126
|
+
this.#view = structuredClone(view);
|
|
127
|
+
this.#observed = observed;
|
|
128
|
+
this.sha256 = hybridDigest(view);
|
|
129
|
+
Object.freeze(this);
|
|
130
|
+
}
|
|
131
|
+
static prepare(context, wave) {
|
|
132
|
+
if (!PreparedCompletedMemoryCorrectionWave.isPrepared(wave))
|
|
133
|
+
refuse("completed_correction_wave_required");
|
|
134
|
+
if (!PreparedRegisteredMemoryCorrections.isPrepared(wave.registration()))
|
|
135
|
+
refuse("first_correction_wave_required");
|
|
136
|
+
if (context.db.isOwnedWriteTransaction?.())
|
|
137
|
+
refuse("preparation_outside_writer_required");
|
|
138
|
+
wave.revalidate(context);
|
|
139
|
+
const view = prepareView(wave), original = wave.registration().review().original(), bundle = original.bundle();
|
|
140
|
+
const scope = { projectId: view.scope.project_id, runId: view.scope.run_id };
|
|
141
|
+
const run = assertPreparedRunBundleSqlAuthority(context, scope, bundle), workspace = bundle.execution.snapshot().scope.workspace;
|
|
142
|
+
const observed = ObservedMemorySources.capture({ workspace, run: run.run_root ?? run.run_dir }, view.run_inputs.map(ref => ({ root: ref.root, path: ref.path })));
|
|
143
|
+
const hashes = observed.hashes();
|
|
144
|
+
if (view.run_inputs.some(ref => hashes.get(memorySourceKey(ref.root, ref.path)) !== ref.sha256))
|
|
145
|
+
refuse("corrected_input_bytes_changed");
|
|
146
|
+
const source = reviewSourceHash(workspace, view.run_inputs.filter(ref => ref.root === "workspace").map(ref => ref.path), view.base);
|
|
147
|
+
if (source !== view.source_sha256)
|
|
148
|
+
refuse("corrected_copy_source_changed");
|
|
149
|
+
const prepared = new PreparedCorrectedMemoryReviewInputs(token, wave, view, observed);
|
|
150
|
+
prepared.assertCustody(context);
|
|
151
|
+
return prepared;
|
|
152
|
+
}
|
|
153
|
+
static prepareFromBundle(context, input) {
|
|
154
|
+
if (!(input.bundle instanceof RetainedExecutionBundle) || input.bundle.descriptor.descriptor.id !== "mb-init")
|
|
155
|
+
refuse("registered_memory_bundle_required");
|
|
156
|
+
const original = PreparedRetainedMemoryReviewAuthority.prepare(context, input.scope, input.bundle);
|
|
157
|
+
const review = PreparedRetainedMemoryReviewResults.prepare(context, original);
|
|
158
|
+
const corrections = PreparedRegisteredMemoryCorrections.restore(context, review);
|
|
159
|
+
return this.prepare(context, PreparedCompletedMemoryCorrectionWave.prepare(context, corrections));
|
|
160
|
+
}
|
|
161
|
+
value() { return structuredClone(this.#view); }
|
|
162
|
+
bundle() { return this.#wave.registration().review().original().bundle(); }
|
|
163
|
+
completedWave() { return this.#wave; }
|
|
164
|
+
verificationProfileHash(context) {
|
|
165
|
+
this.assertCustody(context);
|
|
166
|
+
return this.#wave.registration().review().original().verificationProfileHash(context);
|
|
167
|
+
}
|
|
168
|
+
assertCustody(context) { const wave = this.#wave; wave.assertCustody(context); }
|
|
169
|
+
assertAllocationAllowed(context) { const wave = this.#wave; wave.assertAllocationAllowed(context); }
|
|
170
|
+
revalidate(context) {
|
|
171
|
+
const wave = this.#wave;
|
|
172
|
+
wave.revalidate(context);
|
|
173
|
+
this.#observed.revalidate();
|
|
174
|
+
const workspace = wave.registration().review().original().bundle().execution.snapshot().scope.workspace;
|
|
175
|
+
if (reviewSourceHash(workspace, this.#view.run_inputs.filter(ref => ref.root === "workspace").map(ref => ref.path), this.#view.base) !== this.#view.source_sha256)
|
|
176
|
+
refuse("corrected_copy_source_changed");
|
|
177
|
+
this.assertCustody(context);
|
|
178
|
+
}
|
|
179
|
+
}
|
|
180
|
+
function prepareView(wave) {
|
|
181
|
+
const registration = wave.registration(), review = registration.review(), original = review.original().registration().view;
|
|
182
|
+
const files = wave.workspaceManifest().entries().filter(entry => entry.kind === "file").map(entry => ({ path: entry.path, sha256: entry.sha256 }));
|
|
183
|
+
const receipt = registration.receipt(), batch = registration.batch();
|
|
184
|
+
const repairs = wave.repairResults().map(repair => ({ ...repair,
|
|
185
|
+
unit_ids: batch.works.find(work => receipt.work_ids[work.key] === repair.work_id).payload.memory_repair.unit_ids }));
|
|
186
|
+
// The registered initial copy selects current bytes; its unit metadata also
|
|
187
|
+
// retains historical blueprint evidence, which is not a repair-time file.
|
|
188
|
+
const assignments = correctedMemoryReviewAssignments({ units: original.unit_inventory, initialAssignments: original.assignments, affected: wave.affectedUnitIds(), files, repairs });
|
|
189
|
+
const inputs = new Map();
|
|
190
|
+
for (const ref of [...original.run_inputs.filter(ref => ref.root === "run"), ...review.references().map(ref => reference(ref.result.path, ref.result.sha256, "run")),
|
|
191
|
+
...repairs.map(repair => reference(repair.reference.path, repair.reference.sha256, "run")), ...files.map(file => reference(file.path, file.sha256))]) {
|
|
192
|
+
const key = memorySourceKey(ref.root, ref.path), prior = inputs.get(key);
|
|
193
|
+
if (prior && prior.sha256 !== ref.sha256)
|
|
194
|
+
refuse("conflicting_current_input");
|
|
195
|
+
inputs.set(key, { ...ref });
|
|
196
|
+
}
|
|
197
|
+
if (inputs.size > 4096)
|
|
198
|
+
refuse("unbounded_current_inputs");
|
|
199
|
+
const runInputs = [...inputs.values()].sort((a, b) => `${a.root}:${a.path}`.localeCompare(`${b.root}:${b.path}`, "en"));
|
|
200
|
+
const workspace = review.original().bundle().execution.snapshot().scope.workspace;
|
|
201
|
+
const source = reviewSourceHash(workspace, runInputs.filter(ref => ref.root === "workspace").map(ref => ref.path), original.base);
|
|
202
|
+
const view = { schema_id: "dd-flow/memory-corrected-review-inputs@1", wave: 1, scope: original.scope,
|
|
203
|
+
completed_correction_sha256: wave.sha256, original_review_sha256: review.sha256,
|
|
204
|
+
source_policy: "review-inputs@1", source_sha256: source, base: original.base, run_inputs: runInputs,
|
|
205
|
+
unit_inventory: structuredClone(original.unit_inventory), affected_unit_ids: wave.affectedUnitIds(), assignments,
|
|
206
|
+
instruction: structuredClone(original.instruction), repairs: repairs.map(repair => ({ work_id: repair.work_id, authority_sha256: repair.authority_sha256, result: repair.reference })) };
|
|
207
|
+
if (Buffer.byteLength(JSON.stringify(view)) > 1024 * 1024)
|
|
208
|
+
refuse("unbounded_corrected_view");
|
|
209
|
+
return view;
|
|
210
|
+
}
|