@deksden-com/dd-flow-cli 0.9.0-beta.136 → 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 +12 -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 +56 -3
- package/dist/cli/command-option-descriptions.js +5 -0
- package/dist/cli/executor-onboarding.js +8 -3
- package/dist/cli/flows.js +29 -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/native-hook-ingress.js +1 -1
- package/dist/cli/response-file.js +2 -0
- package/dist/cli/run-cli.js +283 -160
- package/dist/cli/setup.js +9 -7
- package/dist/cli.js +54 -2
- package/dist/domain/stage-handler-identities.js +20 -0
- package/dist/harness-runtime/DD-ZCODE.md +13 -0
- package/dist/harness-runtime/bin/dd-opencode.mjs +3 -3
- 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 +20 -5
- package/dist/harness-runtime/lib/dd-zcode-daemon.mjs +45 -10
- package/dist/harness-runtime/lib/dd-zcode.mjs +7 -3
- 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 +5 -1
- 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/code-checks.js +387 -34
- package/dist/services/controller-child-observer.js +99 -0
- package/dist/services/controller-execution.js +88 -0
- package/dist/services/controller-fanout.js +79 -13
- 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/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/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/hooks.js +24 -6
- package/dist/services/hybrid-contracts.js +31 -3
- package/dist/services/ids.js +29 -1
- package/dist/services/installation-lock.js +7 -4
- 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/projects.js +17 -2
- package/dist/services/recovery-snapshot-database.js +416 -1
- 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 +184 -48
- 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/stage-context.js +9 -1
- package/dist/services/stage-handlers.js +74 -0
- package/dist/services/stage-lifecycle.js +2 -1
- package/dist/services/stage-report-renderer.js +76 -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 +136 -10
- package/dist/services/vnext-merge.js +8 -3
- package/dist/services/vnext-plan-review.js +6 -4
- package/dist/services/vnext-protocolize.js +60 -2
- 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,72 @@
|
|
|
1
|
+
import { AppError } from "../shared/errors.js";
|
|
2
|
+
import { readFlowRunExecutionBundle } from "./runs.js";
|
|
3
|
+
import { PreparedMemoryInitialReviewInputs } from "./memory-review-inputs.js";
|
|
4
|
+
import { readRegisteredMemoryReview, memoryReviewBatch, memoryReviewWorkerSchema } from "./memory-review-registration.js";
|
|
5
|
+
import { hybridDigest } from "./hybrid-contracts.js";
|
|
6
|
+
import { PreparedCorrectedMemoryReviewInputs } from "./memory-corrected-review-inputs.js";
|
|
7
|
+
import { PreparedSecondCorrectedMemoryReviewInputs } from "./memory-second-corrected-review-inputs.js";
|
|
8
|
+
import { PreparedMemoryCorrectionCheckReceipt } from "./memory-correction-check-receipt.js";
|
|
9
|
+
import { PreparedRegisteredCorrectedMemoryReview } from "./memory-corrected-review-registration.js";
|
|
10
|
+
const preparedOwners = new WeakMap();
|
|
11
|
+
function workScope(work) { return hybridDigest([work.work_id, work.project_id, work.run_id, work.parent_work_id, work.payload_json, work.result_schema, work.launch_policy]); }
|
|
12
|
+
export function assertPreparedMemoryReviewWorkInput(context, owner, work, run, generation) {
|
|
13
|
+
const issued = preparedOwners.get(owner);
|
|
14
|
+
if (!issued || issued.store !== context.db || issued.work !== workScope(work) || issued.root !== run.project_root || issued.generation !== generation || issued.input !== hybridDigest(owner.input))
|
|
15
|
+
refuse("foreign_prepared_review_input");
|
|
16
|
+
owner.assertCustody(context);
|
|
17
|
+
}
|
|
18
|
+
function refuse(reason) {
|
|
19
|
+
throw new AppError("memory_review_input_invalid", "Memory reviewer Work lacks its independently registered input", 2, { reason, effect: "no_effect" });
|
|
20
|
+
}
|
|
21
|
+
/** Physical preparation outside a writer. The SQL-only closure must also be
|
|
22
|
+
* consumed by Work claim, not just by an external launch/copy precheck.
|
|
23
|
+
*/
|
|
24
|
+
export function prepareMemoryReviewWorkInput(context, work, run, generation) {
|
|
25
|
+
const bundle = readFlowRunExecutionBundle(context, { projectRoot: run.project_root, runId: work.run_id });
|
|
26
|
+
if (!bundle || bundle.descriptor.descriptor.id !== "mb-init")
|
|
27
|
+
refuse("registered_memory_bundle_required");
|
|
28
|
+
let payload;
|
|
29
|
+
try {
|
|
30
|
+
payload = JSON.parse(work.payload_json ?? "null");
|
|
31
|
+
}
|
|
32
|
+
catch {
|
|
33
|
+
return refuse("invalid_review_payload");
|
|
34
|
+
}
|
|
35
|
+
const packet = payload?.memory_review;
|
|
36
|
+
if (!payload || payload.kind !== "memory-review" || payload.read_only !== true
|
|
37
|
+
|| !((packet?.schema_id === "dd-flow/memory-review-work-packet@1" && packet.wave === 0)
|
|
38
|
+
|| (packet?.schema_id === "dd-flow/memory-review-work-packet@2" && (packet.wave === 1 || packet.wave === 2))))
|
|
39
|
+
refuse("invalid_review_payload");
|
|
40
|
+
const scope = { projectId: work.project_id, runId: work.run_id };
|
|
41
|
+
const corrected = packet.wave === 2 ? PreparedSecondCorrectedMemoryReviewInputs.prepareFromBundle(context, { scope, bundle })
|
|
42
|
+
: packet.wave === 1 ? PreparedCorrectedMemoryReviewInputs.prepareFromBundle(context, { scope, bundle }) : null;
|
|
43
|
+
const registration = corrected ? PreparedRegisteredCorrectedMemoryReview.restore(context, corrected, PreparedMemoryCorrectionCheckReceipt.restore(context, corrected)) : null;
|
|
44
|
+
const owner = corrected ?? PreparedMemoryInitialReviewInputs.prepare(context, { scope, bundle });
|
|
45
|
+
const view = owner.value(), receipt = registration ? registration.receipt() : readRegisteredMemoryReview(context, view.scope, view.wave);
|
|
46
|
+
if (receipt.input_sha256 !== owner.sha256 || work.parent_work_id !== view.scope.parent_work_id
|
|
47
|
+
|| work.result_schema !== memoryReviewWorkerSchema || work.launch_policy !== "fresh_agent_required")
|
|
48
|
+
refuse("foreign_review_assignment");
|
|
49
|
+
const batch = registration ? registration.batch() : memoryReviewBatch(owner.value());
|
|
50
|
+
const assigned = batch.works.find(item => receipt.work_ids[item.key] === work.work_id);
|
|
51
|
+
if (!assigned || !payload || payload.kind !== "memory-review" || payload.read_only !== true
|
|
52
|
+
|| hybridDigest(payload.memory_review) !== hybridDigest(assigned.payload.memory_review))
|
|
53
|
+
refuse("changed_review_assignment");
|
|
54
|
+
const receiptSha256 = hybridDigest(receipt);
|
|
55
|
+
const assertCustody = (writer) => {
|
|
56
|
+
owner.assertCustody(writer);
|
|
57
|
+
if (registration)
|
|
58
|
+
registration.assertCustody(writer);
|
|
59
|
+
else if (hybridDigest(readRegisteredMemoryReview(writer, view.scope, view.wave)) !== receiptSha256)
|
|
60
|
+
refuse("review_registration_changed");
|
|
61
|
+
};
|
|
62
|
+
assertCustody(context);
|
|
63
|
+
const input = { schema_id: "dd-flow/review-input@3", parent_work_id: view.scope.parent_work_id,
|
|
64
|
+
kind: "memory-review", cycle: `memory-review-wave-${view.wave}`, generation,
|
|
65
|
+
workspace_fingerprint: view.source_sha256, source_policy: "review-inputs@1",
|
|
66
|
+
source_sha256: view.source_sha256, run_inputs: structuredClone(view.run_inputs) };
|
|
67
|
+
const prepared = Object.freeze({ input, assertCustody, bundle,
|
|
68
|
+
registration: () => structuredClone(receipt), packet: () => structuredClone(assigned.payload.memory_review),
|
|
69
|
+
revalidate: (reader) => { owner.revalidate(reader); registration?.revalidate(reader); assertCustody(reader); } });
|
|
70
|
+
preparedOwners.set(prepared, { store: context.db, work: workScope(work), root: run.project_root, generation, input: hybridDigest(input) });
|
|
71
|
+
return prepared;
|
|
72
|
+
}
|
|
@@ -0,0 +1,73 @@
|
|
|
1
|
+
import { Ajv2020 } from "ajv/dist/2020.js";
|
|
2
|
+
import { createHash } from "node:crypto";
|
|
3
|
+
import { AppError } from "../shared/errors.js";
|
|
4
|
+
import { AdmittedFrozenEngine } from "./frozen-engine.js";
|
|
5
|
+
import { memorySourceKey } from "./memory-stage-results.js";
|
|
6
|
+
import { memoryReviewWorkerSchema } from "./memory-review-registration.js";
|
|
7
|
+
function refuse(reason) {
|
|
8
|
+
throw new AppError("memory_review_work_result_invalid", "Reviewer result lacks assigned unit and retained input evidence", 2, { reason, effect: "no_effect" });
|
|
9
|
+
}
|
|
10
|
+
/** Frozen schema and claim admission only. The registered Work owner selects
|
|
11
|
+
* the packet/references and must still prove actual independent Session,
|
|
12
|
+
* unchanged copy, generation and atomic settlement. This executes no checks.
|
|
13
|
+
*/
|
|
14
|
+
export function validateMemoryReviewWorkResult(input, engine, binding) {
|
|
15
|
+
try {
|
|
16
|
+
if (!(input instanceof Uint8Array) || !input.byteLength || input.byteLength > 1024 * 1024
|
|
17
|
+
|| !(engine instanceof AdmittedFrozenEngine) || !binding.workId || binding.packet?.result_schema !== memoryReviewWorkerSchema)
|
|
18
|
+
refuse("invalid_preparation_binding");
|
|
19
|
+
const value = JSON.parse(new TextDecoder("utf-8", { fatal: true }).decode(input));
|
|
20
|
+
const schema = JSON.parse(new TextDecoder("utf-8", { fatal: true }).decode(engine.schema("memory-review-work-result")));
|
|
21
|
+
if (schema.$id !== memoryReviewWorkerSchema)
|
|
22
|
+
refuse("invalid_result_schema");
|
|
23
|
+
const packet = binding.packet;
|
|
24
|
+
if (value.schema_id !== memoryReviewWorkerSchema || value.run_id !== packet.run_id || value.work_id !== binding.workId || value.packet_sha256 !== packet.packet_sha256
|
|
25
|
+
|| value.unit_id !== packet.unit.id || value.wave !== packet.wave)
|
|
26
|
+
refuse("foreign_result_identity");
|
|
27
|
+
const validate = new Ajv2020({ strict: true, validateFormats: false }).compile(schema);
|
|
28
|
+
if (!validate(value))
|
|
29
|
+
throw new AppError("memory_review_work_result_invalid", "Reviewer result proposal does not match its schema; correct the reported fields using the assigned packet references", 2, { reason: "invalid_result_schema", phase: "prepare", effect: "no_effect", recoverable: true,
|
|
30
|
+
errors: structuredClone(validate.errors), result_sha256: createHash("sha256").update(input).digest("hex") });
|
|
31
|
+
const sources = new Set([...binding.references, ...packet.unit.evidence.map(ref => ({ root: ref.root === 'run' ? 'run' : 'workspace',
|
|
32
|
+
path: ref.root === 'canon' ? `.memory-bank/${ref.path}` : ref.path, sha256: ref.sha256 }))].map(ref => `${memorySourceKey(ref.root, ref.path)}\0${ref.sha256}`));
|
|
33
|
+
const evidence = (refs) => {
|
|
34
|
+
for (const ref of refs) {
|
|
35
|
+
if (!sources.has(`${memorySourceKey(ref.root, ref.path)}\0${ref.sha256}`))
|
|
36
|
+
refuse("unregistered_or_duplicate_evidence");
|
|
37
|
+
}
|
|
38
|
+
};
|
|
39
|
+
const nonblank = (text) => Boolean(text.trim()) && !text.includes("\0");
|
|
40
|
+
if (!nonblank(value.summary))
|
|
41
|
+
refuse("missing_assessment");
|
|
42
|
+
evidence(value.evidence);
|
|
43
|
+
const outputs = packet.unit.artifacts.map(ref => ({ root: "workspace", path: ref.path, sha256: ref.sha256 }));
|
|
44
|
+
if (!value.evidence.some(ref => outputs.some(output => ref.root === output.root && ref.path === output.path && ref.sha256 === output.sha256))) {
|
|
45
|
+
const corrected = "deleted_references" in packet;
|
|
46
|
+
if (!corrected || packet.schema_id !== "dd-flow/memory-review-work-packet@2" || ![1, 2].includes(packet.wave)
|
|
47
|
+
|| outputs.length || value.outcome === "accepted" || !packet.deleted_references.some(deleted => deleted.role === "artifact"
|
|
48
|
+
&& value.evidence.some(ref => ref.root === "run" && deleted.repair_result.root === "run"
|
|
49
|
+
&& deleted.repair_result.path === ref.path && deleted.repair_result.sha256 === ref.sha256)))
|
|
50
|
+
refuse("assessment_without_unit_output");
|
|
51
|
+
}
|
|
52
|
+
const findingIds = new Set();
|
|
53
|
+
for (const finding of value.findings) {
|
|
54
|
+
if (finding.unit_id !== packet.unit.id || findingIds.has(finding.id))
|
|
55
|
+
refuse("foreign_or_duplicate_finding");
|
|
56
|
+
findingIds.add(finding.id);
|
|
57
|
+
if (!nonblank(finding.summary))
|
|
58
|
+
refuse("missing_finding_problem");
|
|
59
|
+
evidence(finding.evidence);
|
|
60
|
+
if (!finding.evidence.some(ref => packet.unit_inputs.some(source => ref.root === source.root && ref.path === source.path && ref.sha256 === source.sha256)))
|
|
61
|
+
refuse("finding_without_unit_source");
|
|
62
|
+
}
|
|
63
|
+
const material = value.findings.some(finding => finding.severity !== "minor");
|
|
64
|
+
if (value.outcome === "accepted" && material || value.outcome === "needs_repair" && !material)
|
|
65
|
+
refuse("contradictory_review_outcome");
|
|
66
|
+
return structuredClone(value);
|
|
67
|
+
}
|
|
68
|
+
catch (error) {
|
|
69
|
+
if (error instanceof AppError && ["memory_review_work_result_invalid", "frozen_engine_invalid"].includes(error.code))
|
|
70
|
+
throw error;
|
|
71
|
+
return refuse("invalid_result_or_binding");
|
|
72
|
+
}
|
|
73
|
+
}
|
|
@@ -0,0 +1,165 @@
|
|
|
1
|
+
import { AppError } from "../shared/errors.js";
|
|
2
|
+
import { hybridDigest } from "./hybrid-contracts.js";
|
|
3
|
+
import { PreparedCompletedMemoryCorrectionWave, memoryCorrectionCurrentUnits } from "./memory-completed-correction-wave.js";
|
|
4
|
+
import { PreparedRetainedSecondMemoryRepairs } from "./memory-retained-second-repairs.js";
|
|
5
|
+
import { correctedMemoryReviewAssignments } from "./memory-corrected-review-inputs.js";
|
|
6
|
+
import { ObservedMemorySources } from "./memory-source-observations.js";
|
|
7
|
+
import { memorySourceKey } from "./memory-stage-results.js";
|
|
8
|
+
import { assertPreparedRunBundleSqlAuthority } from "./runs.js";
|
|
9
|
+
import { reviewSourceHash } from "./review-copy.js";
|
|
10
|
+
import { RetainedExecutionBundle } from "./execution-bundles.js";
|
|
11
|
+
const token = Symbol("second-corrected-memory-review-inputs");
|
|
12
|
+
function refuse(reason) {
|
|
13
|
+
throw new AppError("memory_second_corrected_review_input_invalid", "Final re-review lacks settled second repairs and independent first-review custody", 2, { reason, effect: "no_effect" });
|
|
14
|
+
}
|
|
15
|
+
/** Metadata projection only; caller must establish first/second custody. */
|
|
16
|
+
export function secondCorrectedMemoryReviewAssignments(input) {
|
|
17
|
+
if (!input.first || !Array.isArray(input.first.unit_inventory) || !input.first.unit_inventory.length || input.first.unit_inventory.length > 256
|
|
18
|
+
|| !Array.isArray(input.first.assignments) || !input.first.assignments.length || input.first.assignments.length > 256
|
|
19
|
+
|| input.first.assignments.some(assignment => !assignment?.unit?.id || !Array.isArray(assignment.deleted_references))
|
|
20
|
+
|| new Set(input.first.assignments.map(assignment => assignment.unit.id)).size !== input.first.assignments.length
|
|
21
|
+
|| input.first.assignments.some(assignment => !input.first.unit_inventory.some(unit => unit.id === assignment.unit.id)))
|
|
22
|
+
refuse("invalid_first_unit_projection");
|
|
23
|
+
const firstAssignments = new Map(input.first.assignments.map(assignment => [assignment.unit.id, assignment]));
|
|
24
|
+
const units = memoryCorrectionCurrentUnits(input.first.unit_inventory, input.first.assignments);
|
|
25
|
+
const assignments = correctedMemoryReviewAssignments({ units, affected: input.affected, files: input.files, repairs: input.repairs });
|
|
26
|
+
const available = new Map(input.files.map(file => [file.path, file]));
|
|
27
|
+
for (const assignment of assignments) {
|
|
28
|
+
for (const deleted of firstAssignments.get(assignment.unit.id)?.deleted_references ?? []) {
|
|
29
|
+
const recreated = available.get(deleted.path);
|
|
30
|
+
if (recreated) {
|
|
31
|
+
const ref = { root: "workspace", path: recreated.path, sha256: recreated.sha256 };
|
|
32
|
+
const role = deleted.role === "artifact" ? assignment.unit.artifacts : assignment.unit.evidence;
|
|
33
|
+
if (!role.some(item => item.path === ref.path))
|
|
34
|
+
role.push({ ...ref });
|
|
35
|
+
if (!assignment.inputs.some(item => item.root === ref.root && item.path === ref.path))
|
|
36
|
+
assignment.inputs.push({ ...ref });
|
|
37
|
+
continue;
|
|
38
|
+
}
|
|
39
|
+
if (assignment.deleted_references.some(item => item.role === deleted.role && item.path === deleted.path))
|
|
40
|
+
continue;
|
|
41
|
+
assignment.deleted_references.push(structuredClone(deleted));
|
|
42
|
+
const proof = deleted.repair_result, prior = assignment.inputs.find(ref => ref.root === proof.root && ref.path === proof.path);
|
|
43
|
+
if (prior && prior.sha256 !== proof.sha256)
|
|
44
|
+
refuse("conflicting_first_deletion_proof");
|
|
45
|
+
if (!prior)
|
|
46
|
+
assignment.inputs.push(structuredClone(proof));
|
|
47
|
+
}
|
|
48
|
+
assignment.inputs.sort((a, b) => `${a.root}:${a.path}`.localeCompare(`${b.root}:${b.path}`, "en"));
|
|
49
|
+
}
|
|
50
|
+
return { units, assignments };
|
|
51
|
+
}
|
|
52
|
+
/** Pure reference projection shared by live admission and historical replay.
|
|
53
|
+
* The caller must independently establish all first/second settlement proofs. */
|
|
54
|
+
export function secondCorrectedMemoryReviewReferences(input) {
|
|
55
|
+
for (const items of [input.firstInputs, input.firstWorkers, input.repairs, input.files])
|
|
56
|
+
if (!Array.isArray(items) || items.length > 4096)
|
|
57
|
+
refuse("unbounded_second_inputs");
|
|
58
|
+
if (input.firstWorkers.some(worker => !worker || !worker.result) || input.repairs.some(repair => !repair || !repair.reference)
|
|
59
|
+
|| input.files.some(file => !file))
|
|
60
|
+
refuse("invalid_second_input_reference");
|
|
61
|
+
const inputs = new Map();
|
|
62
|
+
const valid = (ref) => {
|
|
63
|
+
if (!ref || !["run", "workspace"].includes(ref.root) || typeof ref.path !== "string" || !ref.path
|
|
64
|
+
|| ref.path.startsWith("/") || ref.path.includes("\\") || ref.path.includes("\0") || ref.path.length > 4096
|
|
65
|
+
|| ref.path.split("/").some(part => !part || part === "." || part === "..")
|
|
66
|
+
|| typeof ref.sha256 !== "string" || !/^[a-f0-9]{64}$/.test(ref.sha256))
|
|
67
|
+
refuse("invalid_second_input_reference");
|
|
68
|
+
};
|
|
69
|
+
for (const ref of input.firstInputs)
|
|
70
|
+
valid(ref);
|
|
71
|
+
const references = [...input.firstInputs.filter(ref => ref.root === "run"),
|
|
72
|
+
...input.firstWorkers.map(worker => ({ root: "run", ...worker.result })),
|
|
73
|
+
...input.repairs.map(repair => ({ root: "run", ...repair.reference })),
|
|
74
|
+
...input.files.map(file => ({ root: "workspace", ...file }))];
|
|
75
|
+
for (const ref of references) {
|
|
76
|
+
valid(ref);
|
|
77
|
+
const key = memorySourceKey(ref.root, ref.path), prior = inputs.get(key);
|
|
78
|
+
if (prior && prior.sha256 !== ref.sha256)
|
|
79
|
+
refuse("conflicting_second_input");
|
|
80
|
+
inputs.set(key, { root: ref.root, path: ref.path, sha256: ref.sha256 });
|
|
81
|
+
}
|
|
82
|
+
if (inputs.size > 4096)
|
|
83
|
+
refuse("unbounded_second_inputs");
|
|
84
|
+
return [...inputs.values()].sort((a, b) => `${a.root}:${a.path}`.localeCompare(`${b.root}:${b.path}`, "en"));
|
|
85
|
+
}
|
|
86
|
+
/** Second selective input custody only. Fresh checks and independently
|
|
87
|
+
* registered read-only reviewers must consume it before semantic closure. */
|
|
88
|
+
export class PreparedSecondCorrectedMemoryReviewInputs {
|
|
89
|
+
static isPrepared(value) {
|
|
90
|
+
return typeof value === "object" && value !== null && #wave in value;
|
|
91
|
+
}
|
|
92
|
+
sha256;
|
|
93
|
+
#wave;
|
|
94
|
+
#view;
|
|
95
|
+
#observed;
|
|
96
|
+
constructor(key, wave, view, observed) {
|
|
97
|
+
if (key !== token)
|
|
98
|
+
refuse("second_input_token_required");
|
|
99
|
+
this.#wave = wave;
|
|
100
|
+
this.#view = structuredClone(view);
|
|
101
|
+
this.#observed = observed;
|
|
102
|
+
this.sha256 = hybridDigest(view);
|
|
103
|
+
Object.freeze(this);
|
|
104
|
+
}
|
|
105
|
+
static prepare(context, wave) {
|
|
106
|
+
if (!PreparedCompletedMemoryCorrectionWave.isPrepared(wave))
|
|
107
|
+
refuse("completed_second_wave_required");
|
|
108
|
+
if (!PreparedRetainedSecondMemoryRepairs.isPrepared(wave.registration()))
|
|
109
|
+
refuse("second_registration_required");
|
|
110
|
+
if (context.db.isOwnedWriteTransaction?.())
|
|
111
|
+
refuse("preparation_outside_writer_required");
|
|
112
|
+
wave.revalidate(context);
|
|
113
|
+
const view = prepareView(context, wave), bundle = wave.registration().bundle(), workspace = bundle.execution.snapshot().scope.workspace;
|
|
114
|
+
const run = assertPreparedRunBundleSqlAuthority(context, { projectId: view.scope.project_id, runId: view.scope.run_id }, bundle);
|
|
115
|
+
const observed = ObservedMemorySources.capture({ workspace, run: run.run_root ?? run.run_dir }, view.run_inputs.map(ref => ({ root: ref.root, path: ref.path })));
|
|
116
|
+
const hashes = observed.hashes();
|
|
117
|
+
if (view.run_inputs.some(ref => hashes.get(memorySourceKey(ref.root, ref.path)) !== ref.sha256))
|
|
118
|
+
refuse("second_input_bytes_changed");
|
|
119
|
+
const prepared = new PreparedSecondCorrectedMemoryReviewInputs(token, wave, view, observed);
|
|
120
|
+
prepared.revalidate(context);
|
|
121
|
+
return prepared;
|
|
122
|
+
}
|
|
123
|
+
static prepareFromBundle(context, input) {
|
|
124
|
+
if (!(input.bundle instanceof RetainedExecutionBundle) || input.bundle.descriptor.descriptor.id !== "mb-init")
|
|
125
|
+
refuse("registered_memory_bundle_required");
|
|
126
|
+
const registration = PreparedRetainedSecondMemoryRepairs.prepare(context, input);
|
|
127
|
+
return this.prepare(context, PreparedCompletedMemoryCorrectionWave.prepare(context, registration));
|
|
128
|
+
}
|
|
129
|
+
value() { return structuredClone(this.#view); }
|
|
130
|
+
bundle() { return this.#wave.registration().bundle(); }
|
|
131
|
+
completedWave() { return this.#wave; }
|
|
132
|
+
verificationProfileHash(context) { this.assertCustody(context); return this.#wave.registration().verificationProfileHash(context); }
|
|
133
|
+
assertCustody(context) { this.#wave.assertCustody(context); }
|
|
134
|
+
assertAllocationAllowed(context) { this.#wave.assertAllocationAllowed(context); }
|
|
135
|
+
revalidate(context) {
|
|
136
|
+
this.#wave.revalidate(context);
|
|
137
|
+
this.#observed.revalidate();
|
|
138
|
+
const view = this.#view, workspace = this.bundle().execution.snapshot().scope.workspace;
|
|
139
|
+
if (reviewSourceHash(workspace, view.run_inputs.filter(ref => ref.root === "workspace").map(ref => ref.path), view.base) !== view.source_sha256)
|
|
140
|
+
refuse("second_copy_source_changed");
|
|
141
|
+
this.assertCustody(context);
|
|
142
|
+
}
|
|
143
|
+
}
|
|
144
|
+
function prepareView(context, wave) {
|
|
145
|
+
const registration = wave.registration(), receipt = registration.receipt(), first = registration.initialInputView(), batch = registration.batch();
|
|
146
|
+
const files = wave.workspaceManifest().entries().filter(entry => entry.kind === "file").map(entry => ({ path: entry.path, sha256: entry.sha256 }));
|
|
147
|
+
const repairs = wave.repairResults().map(repair => ({ ...repair,
|
|
148
|
+
unit_ids: batch.works.find(work => receipt.work_ids[work.key] === repair.work_id).payload.memory_repair.unit_ids }));
|
|
149
|
+
// The first re-review's current unit definitions, not obsolete compose-era
|
|
150
|
+
// byte identities, form the baseline for the second delta projection.
|
|
151
|
+
const { units, assignments } = secondCorrectedMemoryReviewAssignments({ first, affected: wave.affectedUnitIds(), files, repairs });
|
|
152
|
+
const proof = registration.firstReviewProof(context);
|
|
153
|
+
const runInputs = secondCorrectedMemoryReviewReferences({ firstInputs: first.run_inputs, firstWorkers: proof.workers, repairs, files });
|
|
154
|
+
const workspace = registration.bundle().execution.snapshot().scope.workspace;
|
|
155
|
+
const view = { schema_id: "dd-flow/memory-corrected-review-inputs@2", wave: 2, scope: first.scope,
|
|
156
|
+
completed_correction_sha256: wave.sha256, original_review_sha256: receipt.review_sha256,
|
|
157
|
+
first_review_input_sha256: receipt.first_review_input_sha256, first_check_receipt_sha256: receipt.first_check_receipt_sha256,
|
|
158
|
+
first_review_registration_sha256: receipt.first_review_registration_sha256,
|
|
159
|
+
source_policy: "review-inputs@1", source_sha256: reviewSourceHash(workspace, runInputs.filter(ref => ref.root === "workspace").map(ref => ref.path), first.base),
|
|
160
|
+
base: first.base, run_inputs: runInputs, unit_inventory: units, affected_unit_ids: wave.affectedUnitIds(), assignments,
|
|
161
|
+
instruction: structuredClone(first.instruction), repairs: repairs.map(repair => ({ work_id: repair.work_id, authority_sha256: repair.authority_sha256, result: repair.reference })) };
|
|
162
|
+
if (Buffer.byteLength(JSON.stringify(view)) > 1024 * 1024)
|
|
163
|
+
refuse("unbounded_second_view");
|
|
164
|
+
return view;
|
|
165
|
+
}
|
|
@@ -0,0 +1,76 @@
|
|
|
1
|
+
import { createHash } from "node:crypto";
|
|
2
|
+
import { AppError } from "../shared/errors.js";
|
|
3
|
+
import { PreparedCorrectedMemoryReviewAcceptance } from "./memory-corrected-review-acceptance.js";
|
|
4
|
+
import { validateMemoryReviewCorrectionPlan } from "./memory-review-correction-plan.js";
|
|
5
|
+
import { PreparedHistoricalOriginalMemoryReview } from "./memory-historical-original-review.js";
|
|
6
|
+
import { PreparedHistoricalCorrectedMemoryReview } from "./memory-historical-corrected-review.js";
|
|
7
|
+
import { hybridDigest } from "./hybrid-contracts.js";
|
|
8
|
+
const token = Symbol("memory-second-correction-plan");
|
|
9
|
+
function refuse(reason) {
|
|
10
|
+
throw new AppError("memory_second_correction_invalid", "Second correction requires independent settled first-wave review", 2, { reason, effect: "no_effect" });
|
|
11
|
+
}
|
|
12
|
+
/** Admission only. Does not allocate repair Works, mutate documents or close
|
|
13
|
+
* review. Wave two binds the effective first-wave findings and source units,
|
|
14
|
+
* not obsolete initial findings or caller-supplied coverage. */
|
|
15
|
+
export class PreparedMemorySecondCorrectionPlan {
|
|
16
|
+
static isPrepared(value) {
|
|
17
|
+
return typeof value === "object" && value !== null && #review in value;
|
|
18
|
+
}
|
|
19
|
+
sha256;
|
|
20
|
+
#review;
|
|
21
|
+
#bytes;
|
|
22
|
+
#value;
|
|
23
|
+
constructor(key, review, bytes, value) {
|
|
24
|
+
if (key !== token)
|
|
25
|
+
refuse("prepared_plan_token_required");
|
|
26
|
+
this.#review = review;
|
|
27
|
+
this.#bytes = bytes;
|
|
28
|
+
this.#value = structuredClone(value);
|
|
29
|
+
this.sha256 = createHash("sha256").update(bytes).digest("hex");
|
|
30
|
+
Object.freeze(this);
|
|
31
|
+
}
|
|
32
|
+
static prepare(context, review, bytes) {
|
|
33
|
+
if (!PreparedCorrectedMemoryReviewAcceptance.isPrepared(review))
|
|
34
|
+
refuse("prepared_first_wave_review_required");
|
|
35
|
+
if (context.db.isOwnedWriteTransaction?.())
|
|
36
|
+
refuse("preparation_outside_writer_required");
|
|
37
|
+
review.revalidate(context);
|
|
38
|
+
const registration = review.registration(), view = registration.receipt().view;
|
|
39
|
+
if (view.wave !== 1)
|
|
40
|
+
refuse("correction_wave_limit");
|
|
41
|
+
const projection = review.resultProjection();
|
|
42
|
+
if (projection.outcome === "blocked")
|
|
43
|
+
refuse("blocked_review_requires_resolution");
|
|
44
|
+
const assignments = new Map(view.assignments.map(assignment => [assignment.unit.id, assignment.unit]));
|
|
45
|
+
const units = view.unit_inventory.map(unit => assignments.get(unit.id) ?? unit);
|
|
46
|
+
const bundle = registration.inputs().bundle();
|
|
47
|
+
const value = validateMemoryReviewCorrectionPlan(bytes, bundle.engine(), { runId: view.scope.run_id, wave: 2,
|
|
48
|
+
reviewSha256: review.sha256, units, findings: projection.findings });
|
|
49
|
+
for (const work of value.works)
|
|
50
|
+
bundle.execution.work("review", work.profile_id ?? undefined);
|
|
51
|
+
const prepared = new PreparedMemorySecondCorrectionPlan(token, review, Buffer.from(bytes), value);
|
|
52
|
+
prepared.historicalReview(context);
|
|
53
|
+
return prepared;
|
|
54
|
+
}
|
|
55
|
+
bytes() { return Buffer.from(this.#bytes); }
|
|
56
|
+
value() { return structuredClone(this.#value); }
|
|
57
|
+
review() { return this.#review; }
|
|
58
|
+
/** Prepare retained proof before second-wave mutation. Expected identities
|
|
59
|
+
* come only from this genuine live admission; no caller-selected receipt
|
|
60
|
+
* hash can substitute for the independently registered first review. */
|
|
61
|
+
historicalReview(context) {
|
|
62
|
+
this.revalidate(context);
|
|
63
|
+
const registration = this.#review.registration(), receipt = registration.receipt(), scope = receipt.scope;
|
|
64
|
+
const original = PreparedHistoricalOriginalMemoryReview.prepareWhileReviewOpen(context, {
|
|
65
|
+
scope: { projectId: scope.project_id, runId: scope.run_id }, bundle: registration.inputs().bundle()
|
|
66
|
+
});
|
|
67
|
+
const historical = PreparedHistoricalCorrectedMemoryReview.prepare(context, { original,
|
|
68
|
+
correctedInputSha256: receipt.input_sha256, checkReceiptSha256: receipt.check_receipt_sha256 });
|
|
69
|
+
if (historical.reviewSha256() !== this.#review.sha256
|
|
70
|
+
|| hybridDigest(historical.projection()) !== hybridDigest(this.#review.resultProjection()))
|
|
71
|
+
refuse("historical_first_wave_changed");
|
|
72
|
+
return historical;
|
|
73
|
+
}
|
|
74
|
+
assertCustody(context) { this.#review.assertCustody(context); }
|
|
75
|
+
revalidate(context) { this.#review.revalidate(context); }
|
|
76
|
+
}
|
|
@@ -0,0 +1,91 @@
|
|
|
1
|
+
import { AppError } from "../shared/errors.js";
|
|
2
|
+
import { appendAudit } from "./audit.js";
|
|
3
|
+
import { hybridDigest } from "./hybrid-contracts.js";
|
|
4
|
+
import { PreparedSecondMemoryRepairSource } from "./memory-second-repair-source.js";
|
|
5
|
+
import { MemoryRepairDocumentBaseline } from "./memory-repair-document-delta.js";
|
|
6
|
+
const token = Symbol("second-memory-repair-baseline");
|
|
7
|
+
function refuse(reason) {
|
|
8
|
+
throw new AppError("memory_second_repair_baseline_invalid", "Second writer baseline lacks independently admitted current source", 2, { reason, effect: "no_effect" });
|
|
9
|
+
}
|
|
10
|
+
function selected(context, receipt) {
|
|
11
|
+
const rows = context.db.all("SELECT payload_json FROM audit_events WHERE project_id=? AND event_type='memory.repair_work_baseline' AND json_extract(payload_json,'$.run_id')=? AND json_extract(payload_json,'$.work_id')=? LIMIT 2", [receipt.project_id, receipt.run_id, receipt.work_id]);
|
|
12
|
+
if (!rows.length)
|
|
13
|
+
return null;
|
|
14
|
+
if (rows.length !== 1 || Buffer.byteLength(rows[0].payload_json) > 24 * 1024 * 1024)
|
|
15
|
+
refuse("ambiguous_or_unbounded_baseline");
|
|
16
|
+
try {
|
|
17
|
+
return JSON.parse(rows[0].payload_json);
|
|
18
|
+
}
|
|
19
|
+
catch {
|
|
20
|
+
return refuse("invalid_baseline");
|
|
21
|
+
}
|
|
22
|
+
}
|
|
23
|
+
/** Second-wave pre-launch receipt only. It grants no private copy,
|
|
24
|
+
* Session/start, publication or historical post-write re-admission rights. */
|
|
25
|
+
export class PreparedSecondMemoryRepairBaseline {
|
|
26
|
+
static isPrepared(value) {
|
|
27
|
+
return typeof value === "object" && value !== null && #source in value;
|
|
28
|
+
}
|
|
29
|
+
sha256;
|
|
30
|
+
#source;
|
|
31
|
+
#receipt;
|
|
32
|
+
constructor(key, source, receipt) {
|
|
33
|
+
if (key !== token)
|
|
34
|
+
refuse("baseline_token_required");
|
|
35
|
+
this.#source = source;
|
|
36
|
+
this.#receipt = structuredClone(receipt);
|
|
37
|
+
this.sha256 = hybridDigest(receipt);
|
|
38
|
+
Object.freeze(this);
|
|
39
|
+
}
|
|
40
|
+
static prepare(context, source) {
|
|
41
|
+
if (!PreparedSecondMemoryRepairSource.isPrepared(source))
|
|
42
|
+
refuse("prepared_second_source_required");
|
|
43
|
+
if (context.db.isOwnedWriteTransaction?.())
|
|
44
|
+
refuse("preparation_outside_writer_required");
|
|
45
|
+
source.revalidate(context);
|
|
46
|
+
const baseline = source.baseline(), exported = source.exportedBaseline();
|
|
47
|
+
const binding = source.baselineBinding(context);
|
|
48
|
+
const receipt = { schema_id: binding.current_view_sha256 ? "dd-flow/memory-repair-work-baseline@5" : "dd-flow/memory-repair-work-baseline@4", wave: 2, ...binding,
|
|
49
|
+
baseline_sha256: baseline.sha256, baseline_base64: baseline.bytes().toString("base64"), export_policy: "memory-repair-export@1",
|
|
50
|
+
exported_baseline_sha256: exported.sha256, exported_baseline_base64: exported.bytes().toString("base64") };
|
|
51
|
+
if (Buffer.byteLength(JSON.stringify(receipt)) > 24 * 1024 * 1024)
|
|
52
|
+
refuse("unbounded_baseline");
|
|
53
|
+
source.revalidate(context);
|
|
54
|
+
return new PreparedSecondMemoryRepairBaseline(token, source, receipt);
|
|
55
|
+
}
|
|
56
|
+
receipt() { return structuredClone(this.#receipt); }
|
|
57
|
+
baseline() {
|
|
58
|
+
return MemoryRepairDocumentBaseline.restore(Buffer.from(this.#receipt.baseline_base64, "base64"), this.#receipt.baseline_sha256);
|
|
59
|
+
}
|
|
60
|
+
exportedBaseline() {
|
|
61
|
+
return MemoryRepairDocumentBaseline.restore(Buffer.from(this.#receipt.exported_baseline_base64, "base64"), this.#receipt.exported_baseline_sha256);
|
|
62
|
+
}
|
|
63
|
+
copyInput() { return this.#source.copyInput(); }
|
|
64
|
+
workerRoute() { return this.#source.workerRoute(); }
|
|
65
|
+
assertPrelaunchCustody(context) { this.assertCustody(context); }
|
|
66
|
+
assertRegisteredCustody(context) {
|
|
67
|
+
this.assertCustody(context);
|
|
68
|
+
if (hybridDigest(selected(context, this.#receipt)) !== this.sha256)
|
|
69
|
+
refuse("registered_baseline_required");
|
|
70
|
+
}
|
|
71
|
+
revalidate(context) { this.#source.revalidate(context); this.assertCustody(context); }
|
|
72
|
+
assertCustody(context) {
|
|
73
|
+
this.#source.assertCustody(context);
|
|
74
|
+
if (hybridDigest({ ...this.#receipt, ...this.#source.baselineBinding(context) }) !== this.sha256)
|
|
75
|
+
refuse("source_binding_changed");
|
|
76
|
+
}
|
|
77
|
+
register(context) {
|
|
78
|
+
this.#source.revalidate(context);
|
|
79
|
+
return context.db.writeTransaction(() => {
|
|
80
|
+
this.assertCustody(context);
|
|
81
|
+
const prior = selected(context, this.#receipt);
|
|
82
|
+
if (prior !== null && hybridDigest(prior) !== this.sha256)
|
|
83
|
+
refuse("baseline_replay_conflict");
|
|
84
|
+
if (prior === null)
|
|
85
|
+
appendAudit(context, { projectId: this.#receipt.project_id, eventType: "memory.repair_work_baseline", payload: this.#receipt });
|
|
86
|
+
if (hybridDigest(selected(context, this.#receipt)) !== this.sha256)
|
|
87
|
+
refuse("baseline_receipt_missing");
|
|
88
|
+
return { work_id: this.#receipt.work_id, baseline_sha256: this.#receipt.baseline_sha256, receipt_sha256: this.sha256, replayed: prior !== null };
|
|
89
|
+
});
|
|
90
|
+
}
|
|
91
|
+
}
|