@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,413 @@
|
|
|
1
|
+
import { createHash } from "node:crypto";
|
|
2
|
+
import { AppError } from "../shared/errors.js";
|
|
3
|
+
import { appendAudit } from "./audit.js";
|
|
4
|
+
import { hybridDigest } from "./hybrid-contracts.js";
|
|
5
|
+
import { assertRunMutationAllowed } from "./run-recovery-guard.js";
|
|
6
|
+
import { readWorkBatchCustody } from "./work-registry.js";
|
|
7
|
+
import { PreparedRegisteredMemoryCorrections } from "./memory-repair-registration.js";
|
|
8
|
+
import { MemoryRepairDocumentBaseline } from "./memory-repair-document-delta.js";
|
|
9
|
+
import { reviewSourceHash, reviewSourcePaths } from "./review-copy.js";
|
|
10
|
+
import { memoryRepairCurrentReferences, validateMemoryRepairWorkResult } from "./memory-repair-work-results.js";
|
|
11
|
+
import { PreparedMemoryRepairCurrentView } from "./memory-repair-current-view.js";
|
|
12
|
+
import { readMemoryRepairPhysicalBinding } from "./memory-repair-physical-binding.js";
|
|
13
|
+
const event = "memory.repair_work_baseline";
|
|
14
|
+
const preparationToken = Symbol("memory-repair-work-baseline-preparation");
|
|
15
|
+
function refuse(reason) {
|
|
16
|
+
throw new AppError("memory_repair_work_baseline_invalid", "Writer baseline lacks registered repair Work and current source custody", 1, { reason, effect: "no_effect" });
|
|
17
|
+
}
|
|
18
|
+
function select(context, scope) {
|
|
19
|
+
const rows = context.db.all("SELECT payload_json FROM audit_events WHERE project_id=? AND event_type=? AND json_extract(payload_json,'$.run_id')=? AND json_extract(payload_json,'$.work_id')=? LIMIT 2", [scope.project_id, event, scope.run_id, scope.work_id]);
|
|
20
|
+
if (!rows.length)
|
|
21
|
+
return null;
|
|
22
|
+
try {
|
|
23
|
+
if (rows.length !== 1 || Buffer.byteLength(rows[0].payload_json) > 24 * 1024 * 1024)
|
|
24
|
+
refuse("ambiguous_or_unbounded_baseline");
|
|
25
|
+
const receipt = JSON.parse(rows[0].payload_json);
|
|
26
|
+
if (!["dd-flow/memory-repair-work-baseline@2", "dd-flow/memory-repair-work-baseline@3"].includes(receipt.schema_id) || receipt.export_policy !== "memory-repair-export@1"
|
|
27
|
+
|| receipt.project_id !== scope.project_id
|
|
28
|
+
|| receipt.run_id !== scope.run_id || receipt.work_id !== scope.work_id || receipt.generation !== scope.generation
|
|
29
|
+
|| typeof receipt.baseline_base64 !== "string")
|
|
30
|
+
refuse("foreign_baseline_receipt");
|
|
31
|
+
if (receipt.schema_id === "dd-flow/memory-repair-work-baseline@3"
|
|
32
|
+
? [receipt.current_view_sha256, receipt.current_view_receipt_sha256].some(value => typeof value !== "string" || !/^[a-f0-9]{64}$/.test(value))
|
|
33
|
+
: Object.hasOwn(receipt, "current_view_sha256") || Object.hasOwn(receipt, "current_view_receipt_sha256"))
|
|
34
|
+
refuse("invalid_current_view_binding");
|
|
35
|
+
const bytes = Buffer.from(receipt.baseline_base64, "base64");
|
|
36
|
+
if (bytes.toString("base64") !== receipt.baseline_base64)
|
|
37
|
+
refuse("changed_baseline_bytes");
|
|
38
|
+
MemoryRepairDocumentBaseline.restore(bytes, receipt.baseline_sha256);
|
|
39
|
+
if (typeof receipt.exported_baseline_base64 !== "string")
|
|
40
|
+
refuse("exported_baseline_receipt_required");
|
|
41
|
+
const exportedBytes = Buffer.from(receipt.exported_baseline_base64, "base64");
|
|
42
|
+
if (exportedBytes.toString("base64") !== receipt.exported_baseline_base64)
|
|
43
|
+
refuse("changed_exported_baseline_bytes");
|
|
44
|
+
const exported = MemoryRepairDocumentBaseline.restore(exportedBytes, receipt.exported_baseline_sha256);
|
|
45
|
+
const source = MemoryRepairDocumentBaseline.restore(bytes, receipt.baseline_sha256);
|
|
46
|
+
if (source.exported(exported.entries().filter(entry => entry.kind === "file").map(entry => entry.path)).sha256 !== exported.sha256)
|
|
47
|
+
refuse("foreign_exported_baseline");
|
|
48
|
+
return receipt;
|
|
49
|
+
}
|
|
50
|
+
catch (error) {
|
|
51
|
+
if (error instanceof AppError && error.code === "memory_repair_work_baseline_invalid")
|
|
52
|
+
throw error;
|
|
53
|
+
return refuse("invalid_baseline_receipt");
|
|
54
|
+
}
|
|
55
|
+
}
|
|
56
|
+
/** Pre-launch original-document admission. Its independent receipt is not a
|
|
57
|
+
* WorkSession/start/copy or publication receipt. Dependent/previously repaired
|
|
58
|
+
* document views must come from registered physical repair settlements, never
|
|
59
|
+
* from current workspace bytes alone. Those views remain a separate owner.
|
|
60
|
+
*/
|
|
61
|
+
export class PreparedMemoryRepairWorkBaseline {
|
|
62
|
+
static isPrepared(value) {
|
|
63
|
+
return typeof value === "object" && value !== null && #receipt in value;
|
|
64
|
+
}
|
|
65
|
+
workId;
|
|
66
|
+
sha256;
|
|
67
|
+
#registration;
|
|
68
|
+
#allocation;
|
|
69
|
+
#scope;
|
|
70
|
+
#receipt;
|
|
71
|
+
#baseline;
|
|
72
|
+
#launch;
|
|
73
|
+
#startedWorkSessionId;
|
|
74
|
+
#currentView;
|
|
75
|
+
constructor(token, registration, allocation, receipt, baseline, launch = null, startedWorkSessionId = null, currentView = null) {
|
|
76
|
+
if (token !== preparationToken)
|
|
77
|
+
refuse("prepared_baseline_token_required");
|
|
78
|
+
this.#registration = registration;
|
|
79
|
+
this.#allocation = structuredClone(allocation);
|
|
80
|
+
this.#scope = {
|
|
81
|
+
project_id: receipt.project_id, run_id: receipt.run_id, work_id: receipt.work_id, generation: receipt.generation
|
|
82
|
+
};
|
|
83
|
+
this.#receipt = structuredClone(receipt);
|
|
84
|
+
this.#baseline = baseline;
|
|
85
|
+
this.#launch = launch ? structuredClone(launch) : null;
|
|
86
|
+
this.#startedWorkSessionId = startedWorkSessionId;
|
|
87
|
+
this.#currentView = currentView;
|
|
88
|
+
this.workId = receipt.work_id;
|
|
89
|
+
this.sha256 = hybridDigest(receipt);
|
|
90
|
+
Object.freeze(this);
|
|
91
|
+
}
|
|
92
|
+
static prepare(context, registration, workId) {
|
|
93
|
+
if (!(registration instanceof PreparedRegisteredMemoryCorrections))
|
|
94
|
+
refuse("registered_corrections_required");
|
|
95
|
+
registration.revalidate(context);
|
|
96
|
+
const owner = registration.receipt(), assigned = registration.batch().works.find(item => owner.work_ids[item.key] === workId);
|
|
97
|
+
if (!assigned)
|
|
98
|
+
refuse("foreign_repair_work");
|
|
99
|
+
const scope = { project_id: owner.scope.project_id, run_id: owner.run_id, work_id: workId,
|
|
100
|
+
generation: assertRunMutationAllowed(context, owner.scope.project_id, owner.run_id) };
|
|
101
|
+
const allocation = readWorkBatchCustody(context, workId);
|
|
102
|
+
// Never rebase original evidence onto an unexplained shared-workspace edit.
|
|
103
|
+
// The next document-view owner will derive these inputs from settled deltas.
|
|
104
|
+
if (assigned.depends_on.length)
|
|
105
|
+
refuse("registered_dependency_document_view_required");
|
|
106
|
+
const view = registration.review().original().registration().view;
|
|
107
|
+
const workspace = registration.review().original().bundle().execution.snapshot().scope.workspace;
|
|
108
|
+
const sourceHash = () => reviewSourceHash(workspace, view.run_inputs.filter(ref => ref.root === "workspace").map(ref => ref.path), view.base);
|
|
109
|
+
if (sourceHash() !== view.source_sha256)
|
|
110
|
+
refuse("original_source_changed_before_writer");
|
|
111
|
+
const baseline = MemoryRepairDocumentBaseline.capture(workspace, assigned.payload.memory_repair.write_paths);
|
|
112
|
+
const selected = reviewSourcePaths(workspace, view.run_inputs.filter(ref => ref.root === "workspace").map(ref => ref.path), view.base);
|
|
113
|
+
if (selected === null)
|
|
114
|
+
refuse("export_source_inventory_unavailable");
|
|
115
|
+
const available = new Set(baseline.entries().filter(entry => entry.kind === "file").map(entry => entry.path));
|
|
116
|
+
const exported = baseline.exported(selected.filter(logical => logical.startsWith(".memory-bank/") && available.has(logical)));
|
|
117
|
+
if (sourceHash() !== view.source_sha256)
|
|
118
|
+
refuse("original_source_changed_during_baseline");
|
|
119
|
+
baseline.revalidate(workspace);
|
|
120
|
+
const receipt = { schema_id: "dd-flow/memory-repair-work-baseline@2", ...scope,
|
|
121
|
+
registration_sha256: registration.sha256, allocation_sha256: hybridDigest(allocation),
|
|
122
|
+
packet_sha256: assigned.payload.memory_repair.packet_sha256, workspace_root: workspace,
|
|
123
|
+
baseline_sha256: baseline.sha256, baseline_base64: baseline.bytes().toString("base64"),
|
|
124
|
+
export_policy: "memory-repair-export@1", exported_baseline_sha256: exported.sha256,
|
|
125
|
+
exported_baseline_base64: exported.bytes().toString("base64") };
|
|
126
|
+
const prepared = new PreparedMemoryRepairWorkBaseline(preparationToken, registration, allocation, receipt, baseline);
|
|
127
|
+
prepared.assertCustody(context);
|
|
128
|
+
return prepared;
|
|
129
|
+
}
|
|
130
|
+
/** Consume a separately registered current-view receipt. Neither a caller
|
|
131
|
+
* snapshot nor a current filesystem capture can substitute for this owner.
|
|
132
|
+
*/
|
|
133
|
+
static prepareFromCurrentView(context, registration, workId, view) {
|
|
134
|
+
if (!(registration instanceof PreparedRegisteredMemoryCorrections))
|
|
135
|
+
refuse("registered_corrections_required");
|
|
136
|
+
if (!PreparedMemoryRepairCurrentView.isPrepared(view) || view.workId !== workId)
|
|
137
|
+
refuse("registered_current_view_required");
|
|
138
|
+
const retained = PreparedMemoryRepairCurrentView.restore(context, registration, workId);
|
|
139
|
+
if (retained.sha256 !== view.sha256)
|
|
140
|
+
refuse("current_view_registration_changed");
|
|
141
|
+
retained.revalidate(context);
|
|
142
|
+
const owner = registration.receipt(), assigned = registration.batch().works.find(item => owner.work_ids[item.key] === workId);
|
|
143
|
+
if (!assigned)
|
|
144
|
+
refuse("foreign_repair_work");
|
|
145
|
+
const generation = assertRunMutationAllowed(context, owner.scope.project_id, owner.run_id);
|
|
146
|
+
const allocation = readWorkBatchCustody(context, workId), baseline = retained.baseline(), exported = retained.exportedBaseline();
|
|
147
|
+
const receipt = { schema_id: "dd-flow/memory-repair-work-baseline@3", project_id: owner.scope.project_id,
|
|
148
|
+
run_id: owner.run_id, work_id: workId, generation, registration_sha256: registration.sha256,
|
|
149
|
+
allocation_sha256: hybridDigest(allocation), packet_sha256: assigned.payload.memory_repair.packet_sha256,
|
|
150
|
+
workspace_root: registration.review().original().bundle().execution.snapshot().scope.workspace,
|
|
151
|
+
baseline_sha256: baseline.sha256, baseline_base64: baseline.bytes().toString("base64"), export_policy: "memory-repair-export@1",
|
|
152
|
+
exported_baseline_sha256: exported.sha256, exported_baseline_base64: exported.bytes().toString("base64"),
|
|
153
|
+
current_view_sha256: retained.sha256, current_view_receipt_sha256: hybridDigest(retained.receipt()) };
|
|
154
|
+
const prepared = new PreparedMemoryRepairWorkBaseline(preparationToken, registration, allocation, receipt, baseline, null, null, retained);
|
|
155
|
+
prepared.assertCustody(context);
|
|
156
|
+
return prepared;
|
|
157
|
+
}
|
|
158
|
+
receipt() { return structuredClone(this.#receipt); }
|
|
159
|
+
validateResult(context, bytes) {
|
|
160
|
+
this.assertRegisteredCustody(context);
|
|
161
|
+
const owner = this.#registration.receipt();
|
|
162
|
+
const assigned = this.#registration.batch().works.find(item => owner.work_ids[item.key] === this.workId);
|
|
163
|
+
if (!assigned || assigned.payload.memory_repair.packet_sha256 !== this.#receipt.packet_sha256)
|
|
164
|
+
refuse("repair_packet_changed");
|
|
165
|
+
return validateMemoryRepairWorkResult(bytes, this.#registration.review().original().bundle().engine(), { workId: this.workId, packet: assigned.payload.memory_repair,
|
|
166
|
+
...(this.#currentView ? { effectiveInputs: memoryRepairCurrentReferences(assigned.payload.memory_repair, this.#currentView.workspaceManifest()) } : {}) });
|
|
167
|
+
}
|
|
168
|
+
currentInput() {
|
|
169
|
+
if (!this.#currentView)
|
|
170
|
+
return null;
|
|
171
|
+
const owner = this.#registration.receipt(), assigned = this.#registration.batch().works.find(item => owner.work_ids[item.key] === this.workId);
|
|
172
|
+
if (!assigned)
|
|
173
|
+
refuse("repair_packet_changed");
|
|
174
|
+
return { current_view_sha256: this.#currentView.sha256, current_view_receipt_sha256: hybridDigest(this.#currentView.receipt()),
|
|
175
|
+
effective_read_inputs: memoryRepairCurrentReferences(assigned.payload.memory_repair, this.#currentView.workspaceManifest()) };
|
|
176
|
+
}
|
|
177
|
+
/** Retain the registered original baseline after a physical launch exists;
|
|
178
|
+
* never capture today's documents as a replacement for its SQL receipt.
|
|
179
|
+
*/
|
|
180
|
+
static restoreForLaunch(context, registration, workId) {
|
|
181
|
+
return this.#restoreRegistered(context, registration, workId, null);
|
|
182
|
+
}
|
|
183
|
+
/** Restore original registered bytes for an existing physical WorkSession.
|
|
184
|
+
* This grants neither another claim nor shared-workspace publication.
|
|
185
|
+
*/
|
|
186
|
+
static restoreForStartedWork(context, registration, workId, workSessionId) {
|
|
187
|
+
if (typeof workSessionId !== "string" || !workSessionId || workSessionId.length > 256)
|
|
188
|
+
refuse("registered_start_session_required");
|
|
189
|
+
return this.#restoreRegistered(context, registration, workId, workSessionId);
|
|
190
|
+
}
|
|
191
|
+
/** SQL restoration for an already reserved publication, not new input
|
|
192
|
+
* observation. Partly published source must be classified by its intent.
|
|
193
|
+
*/
|
|
194
|
+
static restoreForPublication(context, registration, workId, workSessionId) {
|
|
195
|
+
if (typeof workSessionId !== "string" || !workSessionId || workSessionId.length > 256)
|
|
196
|
+
refuse("registered_start_session_required");
|
|
197
|
+
return this.#restoreRegistered(context, registration, workId, workSessionId, true);
|
|
198
|
+
}
|
|
199
|
+
/** Restore only retained start custody for output observation. Shared input
|
|
200
|
+
* admission belongs to a separate publication source-view owner. */
|
|
201
|
+
static restoreForOutput(context, registration, workId, workSessionId) {
|
|
202
|
+
if (typeof workSessionId !== "string" || !workSessionId || workSessionId.length > 256)
|
|
203
|
+
refuse("registered_start_session_required");
|
|
204
|
+
return this.#restoreRegistered(context, registration, workId, workSessionId, false, true);
|
|
205
|
+
}
|
|
206
|
+
static #restoreRegistered(context, registration, workId, workSessionId, publicationOnly = false, outputOnly = false) {
|
|
207
|
+
if (!(registration instanceof PreparedRegisteredMemoryCorrections))
|
|
208
|
+
refuse("registered_corrections_required");
|
|
209
|
+
registration.assertCustody(context);
|
|
210
|
+
const owner = registration.receipt(), assigned = registration.batch().works.find(item => owner.work_ids[item.key] === workId);
|
|
211
|
+
if (!assigned)
|
|
212
|
+
refuse("foreign_repair_work");
|
|
213
|
+
const scope = { project_id: owner.scope.project_id, run_id: owner.run_id, work_id: workId,
|
|
214
|
+
generation: assertRunMutationAllowed(context, owner.scope.project_id, owner.run_id) };
|
|
215
|
+
const receipt = select(context, scope), allocation = readWorkBatchCustody(context, workId);
|
|
216
|
+
const route = registration.review().original().bundle().execution.work("review", assigned.payload.execution_override?.agent_profile_id);
|
|
217
|
+
const launch = readMemoryRepairPhysicalBinding(context, workId, route);
|
|
218
|
+
if (!receipt || receipt.registration_sha256 !== registration.sha256 || receipt.allocation_sha256 !== hybridDigest(allocation)
|
|
219
|
+
|| receipt.packet_sha256 !== assigned.payload.memory_repair.packet_sha256
|
|
220
|
+
|| receipt.workspace_root !== registration.review().original().bundle().execution.snapshot().scope.workspace)
|
|
221
|
+
refuse("registered_baseline_receipt_required");
|
|
222
|
+
if (assigned.depends_on.length && receipt.schema_id !== "dd-flow/memory-repair-work-baseline@3")
|
|
223
|
+
refuse("registered_dependency_document_view_required");
|
|
224
|
+
const currentView = receipt.schema_id === "dd-flow/memory-repair-work-baseline@3"
|
|
225
|
+
? PreparedMemoryRepairCurrentView.restore(context, registration, workId) : null;
|
|
226
|
+
if (currentView && (currentView.sha256 !== receipt.current_view_sha256 || hybridDigest(currentView.receipt()) !== receipt.current_view_receipt_sha256
|
|
227
|
+
|| currentView.baseline().sha256 !== receipt.baseline_sha256 || currentView.exportedBaseline().sha256 !== receipt.exported_baseline_sha256))
|
|
228
|
+
refuse("registered_current_view_changed");
|
|
229
|
+
if (!launch?.provider_session_id || !["launching", "start_requested"].includes(launch.status))
|
|
230
|
+
refuse("registered_physical_launch_required");
|
|
231
|
+
try {
|
|
232
|
+
if (Buffer.byteLength(launch.profile_json) > 1024 * 1024)
|
|
233
|
+
refuse("invalid_launch_profile");
|
|
234
|
+
const profile = JSON.parse(launch.profile_json);
|
|
235
|
+
const frozen = { coordinator: route.coordinator, worker: route.worker,
|
|
236
|
+
delegation: { mode: route.delegation.mode, max_parallel: route.delegation.mode === "external" ? route.delegation.max_parallel : null } };
|
|
237
|
+
if (hybridDigest({ coordinator: profile.coordinator, worker: profile.worker, delegation: profile.delegation }) !== hybridDigest(frozen)
|
|
238
|
+
|| launch.profile_hash !== createHash("sha256").update(JSON.stringify(frozen)).digest("hex"))
|
|
239
|
+
refuse("launch_profile_not_frozen");
|
|
240
|
+
}
|
|
241
|
+
catch (error) {
|
|
242
|
+
if (error instanceof AppError && error.code === "memory_repair_work_baseline_invalid")
|
|
243
|
+
throw error;
|
|
244
|
+
return refuse("invalid_launch_profile");
|
|
245
|
+
}
|
|
246
|
+
const baseline = MemoryRepairDocumentBaseline.restore(Buffer.from(receipt.baseline_base64, "base64"), receipt.baseline_sha256);
|
|
247
|
+
const prepared = new PreparedMemoryRepairWorkBaseline(preparationToken, registration, allocation, receipt, baseline, launch, workSessionId, currentView);
|
|
248
|
+
prepared.assertRegisteredCustody(context);
|
|
249
|
+
if (publicationOnly)
|
|
250
|
+
prepared.registeredPublicationIntent(context, workSessionId);
|
|
251
|
+
else if (outputOnly)
|
|
252
|
+
prepared.assertRegisteredStartCustody(context, workSessionId);
|
|
253
|
+
else
|
|
254
|
+
prepared.revalidate(context);
|
|
255
|
+
return prepared;
|
|
256
|
+
}
|
|
257
|
+
registeredPublicationIntent(context, workSessionId) {
|
|
258
|
+
this.assertRegisteredStartCustody(context, workSessionId);
|
|
259
|
+
const rows = context.db.all("SELECT payload_json FROM audit_events WHERE project_id=? AND event_type='memory.repair_publication_intent' AND json_extract(payload_json,'$.run_id')=? AND json_extract(payload_json,'$.work_id')=? AND json_extract(payload_json,'$.generation')=? LIMIT 2", [this.#scope.project_id, this.#scope.run_id, this.workId, this.#scope.generation]);
|
|
260
|
+
const starts = context.db.all("SELECT payload_json FROM audit_events WHERE project_id=? AND event_type='memory.repair_work_start' AND json_extract(payload_json,'$.run_id')=? AND json_extract(payload_json,'$.work_id')=? AND json_extract(payload_json,'$.generation')=? LIMIT 2", [this.#scope.project_id, this.#scope.run_id, this.workId, this.#scope.generation]);
|
|
261
|
+
try {
|
|
262
|
+
if (rows.length !== 1 || starts.length !== 1 || Buffer.byteLength(rows[0].payload_json) > 160 * 1024 * 1024
|
|
263
|
+
|| Buffer.byteLength(starts[0].payload_json) > 1024 * 1024)
|
|
264
|
+
refuse("registered_publication_intent_required");
|
|
265
|
+
const value = JSON.parse(rows[0].payload_json);
|
|
266
|
+
const start = JSON.parse(starts[0].payload_json);
|
|
267
|
+
if (!["dd-flow/memory-repair-publication-intent@1", "dd-flow/memory-repair-publication-intent@2"].includes(value.schema_id) || value.project_id !== this.#scope.project_id
|
|
268
|
+
|| value.run_id !== this.#scope.run_id || value.work_id !== this.workId || value.generation !== this.#scope.generation
|
|
269
|
+
|| value.work_session_id !== workSessionId || start.work_session_id !== workSessionId
|
|
270
|
+
|| value.start_receipt_sha256 !== hybridDigest(start) || value.source_root !== this.#receipt.workspace_root)
|
|
271
|
+
refuse("publication_baseline_or_start_changed");
|
|
272
|
+
if (value.schema_id === "dd-flow/memory-repair-publication-intent@1") {
|
|
273
|
+
if (value.before_sha256 !== this.#baseline.sha256 || value.before_base64 !== this.#baseline.bytes().toString("base64"))
|
|
274
|
+
refuse("publication_baseline_or_start_changed");
|
|
275
|
+
}
|
|
276
|
+
else {
|
|
277
|
+
// This SQL binding is not source authority. The publication owner must
|
|
278
|
+
// independently restore the graph/histories and private/shared plans.
|
|
279
|
+
const sources = context.db.all("SELECT payload_json FROM audit_events WHERE project_id=? AND event_type='memory.repair_publication_source' AND json_extract(payload_json,'$.run_id')=? AND json_extract(payload_json,'$.work_id')=? AND json_extract(payload_json,'$.generation')=? LIMIT 2", [this.#scope.project_id, this.#scope.run_id, this.workId, this.#scope.generation]);
|
|
280
|
+
if (sources.length !== 1 || Buffer.byteLength(sources[0].payload_json) > 40 * 1024 * 1024)
|
|
281
|
+
refuse("registered_publication_source_required");
|
|
282
|
+
const source = JSON.parse(sources[0].payload_json), { source_view_sha256: sourceSha256, ...body } = source;
|
|
283
|
+
const privatePlan = value.private_plan;
|
|
284
|
+
if (source.schema_id !== "dd-flow/memory-repair-publication-source@1" || source.project_id !== this.#scope.project_id
|
|
285
|
+
|| source.run_id !== this.#scope.run_id || source.work_id !== this.workId || source.generation !== this.#scope.generation
|
|
286
|
+
|| source.work_session_id !== workSessionId || source.registration_sha256 !== this.#registration.sha256
|
|
287
|
+
|| source.baseline_registration_sha256 !== this.sha256 || source.start_receipt_sha256 !== hybridDigest(start)
|
|
288
|
+
|| source.source_root !== this.#receipt.workspace_root || sourceSha256 !== hybridDigest(body)
|
|
289
|
+
|| value.source_view_sha256 !== sourceSha256 || value.source_receipt_sha256 !== hybridDigest(source)
|
|
290
|
+
|| value.before_sha256 !== source.before_sha256 || value.before_base64 !== source.before_base64
|
|
291
|
+
|| !privatePlan || privatePlan.before_sha256 !== this.#baseline.sha256
|
|
292
|
+
|| privatePlan.before_base64 !== this.#baseline.bytes().toString("base64")
|
|
293
|
+
|| value.private_plan_sha256 !== hybridDigest(privatePlan))
|
|
294
|
+
refuse("publication_source_or_private_start_changed");
|
|
295
|
+
}
|
|
296
|
+
return structuredClone(value);
|
|
297
|
+
}
|
|
298
|
+
catch (error) {
|
|
299
|
+
if (error instanceof AppError && error.code === "memory_repair_work_baseline_invalid")
|
|
300
|
+
throw error;
|
|
301
|
+
return refuse("invalid_publication_intent");
|
|
302
|
+
}
|
|
303
|
+
}
|
|
304
|
+
launch() { return this.#launch ? structuredClone(this.#launch) : null; }
|
|
305
|
+
workerRoute() {
|
|
306
|
+
const receipt = this.#registration.receipt();
|
|
307
|
+
const assigned = this.#registration.batch().works.find(item => receipt.work_ids[item.key] === this.workId);
|
|
308
|
+
if (!assigned)
|
|
309
|
+
refuse("foreign_repair_work");
|
|
310
|
+
return this.#registration.review().original().bundle().execution.work("review", assigned.payload.execution_override?.agent_profile_id);
|
|
311
|
+
}
|
|
312
|
+
assertPrelaunchCustody(context) {
|
|
313
|
+
if (this.#launch)
|
|
314
|
+
refuse("use_registered_launch_baseline");
|
|
315
|
+
this.assertCustody(context);
|
|
316
|
+
}
|
|
317
|
+
/** SQL-only post-claim fence for the same prepared physical start. No new
|
|
318
|
+
* baseline can be registered from this phase or from mutable source bytes.
|
|
319
|
+
*/
|
|
320
|
+
assertRegisteredStartCustody(context, workSessionId) {
|
|
321
|
+
if (!this.#launch?.provider_session_id)
|
|
322
|
+
refuse("registered_physical_launch_required");
|
|
323
|
+
this.#registration.assertCustody(context);
|
|
324
|
+
this.#assertCurrentView(context);
|
|
325
|
+
assertRunMutationAllowed(context, this.#scope.project_id, this.#scope.run_id, this.#scope.generation);
|
|
326
|
+
if (hybridDigest(readWorkBatchCustody(context, this.workId)) !== hybridDigest(this.#allocation))
|
|
327
|
+
refuse("repair_allocation_changed");
|
|
328
|
+
const launch = readMemoryRepairPhysicalBinding(context, this.workId, this.workerRoute(), this.#launch.operation_id);
|
|
329
|
+
const baseline = select(context, this.#scope);
|
|
330
|
+
if (!launch || hybridDigest(launch) !== hybridDigest(this.#launch) || !baseline || hybridDigest(baseline) !== this.sha256)
|
|
331
|
+
refuse("registered_start_custody_changed");
|
|
332
|
+
const rows = context.db.all("SELECT ws.id,ws.session_id,ws.hook_event_id,ws.start_receipt_json,s.harness,s.provider_session_id,s.cwd FROM work_sessions ws JOIN works w ON w.work_id=ws.work_id JOIN runs r ON r.project_id=w.project_id AND r.id=w.run_id JOIN sessions s ON s.project_id=w.project_id AND s.session_id=ws.session_id WHERE w.project_id=? AND w.run_id=? AND w.work_id=? AND w.status='running' AND r.status='running' AND r.workspace_root=? AND ws.status='running' AND s.status='active' AND s.current_stage='work' AND s.worker_id=w.work_id AND s.run_id=w.run_id AND s.workspace_path=r.workspace_root AND s.project_root=r.project_root LIMIT 2", [this.#scope.project_id, this.#scope.run_id, this.workId, this.#receipt.workspace_root]);
|
|
333
|
+
if (rows.length !== 1 || rows[0].id !== workSessionId || rows[0].provider_session_id !== this.#launch.provider_session_id)
|
|
334
|
+
refuse("registered_start_session_required");
|
|
335
|
+
return { ...rows[0] };
|
|
336
|
+
}
|
|
337
|
+
baseline() { return MemoryRepairDocumentBaseline.restore(Buffer.from(this.#receipt.baseline_base64, "base64"), this.#receipt.baseline_sha256); }
|
|
338
|
+
exportedBaseline() { return MemoryRepairDocumentBaseline.restore(Buffer.from(this.#receipt.exported_baseline_base64, "base64"), this.#receipt.exported_baseline_sha256); }
|
|
339
|
+
copyInput() {
|
|
340
|
+
if (this.#currentView)
|
|
341
|
+
return this.#currentView.copyInput();
|
|
342
|
+
const view = this.#registration.review().original().registration().view;
|
|
343
|
+
return { source_sha256: view.source_sha256, base: view.base,
|
|
344
|
+
explicit_inputs: view.run_inputs.filter(ref => ref.root === "workspace").map(ref => ref.path) };
|
|
345
|
+
}
|
|
346
|
+
/** Preparation alone is not an independently committed baseline receipt. */
|
|
347
|
+
assertRegisteredCustody(context) {
|
|
348
|
+
this.assertCustody(context);
|
|
349
|
+
const receipt = select(context, this.#scope);
|
|
350
|
+
if (!receipt || hybridDigest(receipt) !== this.sha256)
|
|
351
|
+
refuse("registered_baseline_receipt_required");
|
|
352
|
+
}
|
|
353
|
+
assertCustody(context) {
|
|
354
|
+
this.#assertCurrentView(context);
|
|
355
|
+
if (this.#startedWorkSessionId) {
|
|
356
|
+
this.assertRegisteredStartCustody(context, this.#startedWorkSessionId);
|
|
357
|
+
return;
|
|
358
|
+
}
|
|
359
|
+
this.#registration.assertCustody(context);
|
|
360
|
+
assertRunMutationAllowed(context, this.#scope.project_id, this.#scope.run_id, this.#scope.generation);
|
|
361
|
+
if (hybridDigest(readWorkBatchCustody(context, this.workId)) !== hybridDigest(this.#allocation))
|
|
362
|
+
refuse("repair_allocation_changed");
|
|
363
|
+
const work = context.db.get("SELECT * FROM works WHERE work_id=?", [this.workId]);
|
|
364
|
+
const run = context.db.get("SELECT workspace_root,status FROM runs WHERE project_id=? AND id=?", [this.#scope.project_id, this.#scope.run_id]);
|
|
365
|
+
if (!work || work.status !== "created" || !run || run.status !== "running" || run.workspace_root !== this.#receipt.workspace_root
|
|
366
|
+
|| context.db.get("SELECT 1 FROM work_sessions WHERE work_id=? LIMIT 1", [this.workId]))
|
|
367
|
+
refuse("unstarted_repair_work_required");
|
|
368
|
+
const launch = this.#launch ? readMemoryRepairPhysicalBinding(context, this.workId, this.workerRoute(), this.#launch.operation_id) : context.db.get("SELECT 1 FROM sqlite_master WHERE type='table' AND name='work_launches'")
|
|
369
|
+
? context.db.get("SELECT operation_id,status,profile_json,profile_hash,provider_session_id FROM work_launches WHERE work_id=?", [this.workId]) : undefined;
|
|
370
|
+
if (this.#launch ? !launch || hybridDigest(launch) !== hybridDigest(this.#launch) : Boolean(launch))
|
|
371
|
+
refuse("use_registered_launch_baseline");
|
|
372
|
+
const prior = select(context, this.#scope);
|
|
373
|
+
if (prior && hybridDigest(prior) !== this.sha256)
|
|
374
|
+
refuse("baseline_registration_changed");
|
|
375
|
+
}
|
|
376
|
+
revalidateSource(context) {
|
|
377
|
+
if (this.#currentView) {
|
|
378
|
+
this.#currentView.revalidate(context);
|
|
379
|
+
return;
|
|
380
|
+
}
|
|
381
|
+
this.#registration.revalidate(context);
|
|
382
|
+
this.#baseline.revalidate(this.#receipt.workspace_root);
|
|
383
|
+
const view = this.#registration.review().original().registration().view;
|
|
384
|
+
if (reviewSourceHash(this.#receipt.workspace_root, view.run_inputs.filter(ref => ref.root === "workspace").map(ref => ref.path), view.base)
|
|
385
|
+
!== view.source_sha256)
|
|
386
|
+
refuse("original_source_changed_before_writer");
|
|
387
|
+
}
|
|
388
|
+
revalidate(context) {
|
|
389
|
+
this.revalidateSource(context);
|
|
390
|
+
this.assertCustody(context);
|
|
391
|
+
}
|
|
392
|
+
register(context) {
|
|
393
|
+
this.assertPrelaunchCustody(context);
|
|
394
|
+
this.revalidate(context);
|
|
395
|
+
return context.db.writeTransaction(() => {
|
|
396
|
+
this.assertCustody(context);
|
|
397
|
+
const prior = select(context, this.#scope);
|
|
398
|
+
if (!prior)
|
|
399
|
+
appendAudit(context, { projectId: this.#scope.project_id, eventType: event, payload: this.#receipt });
|
|
400
|
+
if (hybridDigest(select(context, this.#scope)) !== this.sha256)
|
|
401
|
+
refuse("baseline_registration_readback_failed");
|
|
402
|
+
return { work_id: this.workId, baseline_sha256: this.#baseline.sha256, registration_sha256: this.sha256, replayed: Boolean(prior) };
|
|
403
|
+
});
|
|
404
|
+
}
|
|
405
|
+
#assertCurrentView(context) {
|
|
406
|
+
if (this.#receipt.schema_id !== "dd-flow/memory-repair-work-baseline@3")
|
|
407
|
+
return;
|
|
408
|
+
if (!this.#currentView || this.#currentView.sha256 !== this.#receipt.current_view_sha256
|
|
409
|
+
|| hybridDigest(this.#currentView.receipt()) !== this.#receipt.current_view_receipt_sha256)
|
|
410
|
+
refuse("registered_current_view_required");
|
|
411
|
+
this.#currentView.assertCustody(context);
|
|
412
|
+
}
|
|
413
|
+
}
|
|
@@ -0,0 +1,97 @@
|
|
|
1
|
+
import { AppError } from "../shared/errors.js";
|
|
2
|
+
import { PreparedPublishedMemoryRepairWorkResult } from "./memory-repair-work-custody.js";
|
|
3
|
+
import { prepareMemoryRepairPrivateOutputFromStage, prepareMemoryRepairPublicationSourceFromStage, registerMemoryRepairPublicationSourceFromStage, reserveMemoryRepairPublicationAgainstSourceFromStage, reserveMemorySecondRepairPublicationFromStage, restoreMemoryRepairPublicationFromStage, prepareMemorySecondRepairPublicationFromStage, restoreStartedMemorySecondRepairLaunchFromStage } from "./memory-review-handler.js";
|
|
4
|
+
function refuse(reason) {
|
|
5
|
+
throw new AppError("memory_work_result_preparation_required", "Repair completion requires registered private output and physical start custody", 1, { reason, effect: "no_effect" });
|
|
6
|
+
}
|
|
7
|
+
/** Read-only preparation. Existing intents are restored, never reconstructed
|
|
8
|
+
* from today's partially published shared workspace. */
|
|
9
|
+
export function prepareMemoryRepairWorkCompletion(context, work, result) {
|
|
10
|
+
if (context.db.isOwnedWriteTransaction?.())
|
|
11
|
+
refuse("preparation_outside_writer_required");
|
|
12
|
+
const bytes = Buffer.from(result);
|
|
13
|
+
if (!bytes.length || bytes.length > 1024 * 1024)
|
|
14
|
+
refuse("bounded_result_required");
|
|
15
|
+
const run = context.db.get("SELECT project_root FROM runs WHERE project_id=? AND id=?", [work.project_id, work.run_id]);
|
|
16
|
+
const links = context.db.all("SELECT id,start_receipt_json FROM work_sessions WHERE work_id=? AND status='running' LIMIT 2", [work.work_id]);
|
|
17
|
+
if (!run || links.length !== 1)
|
|
18
|
+
refuse("exact_active_work_session_required");
|
|
19
|
+
const link = links[0];
|
|
20
|
+
let wave, workspaceRoot;
|
|
21
|
+
try {
|
|
22
|
+
const payload = JSON.parse(work.payload_json ?? "null");
|
|
23
|
+
if (work.result_schema !== "dd-flow/memory-repair-work-result@1" || payload?.kind !== "memory-repair" || payload.read_only !== false)
|
|
24
|
+
refuse("registered_repair_required");
|
|
25
|
+
wave = payload.memory_repair?.wave;
|
|
26
|
+
if (!link.start_receipt_json || Buffer.byteLength(link.start_receipt_json) > 32 * 1024 * 1024)
|
|
27
|
+
refuse("bounded_start_required");
|
|
28
|
+
const receipt = JSON.parse(link.start_receipt_json);
|
|
29
|
+
if (typeof receipt.context !== "string" || Buffer.byteLength(receipt.context) > 16 * 1024 * 1024)
|
|
30
|
+
refuse("bounded_context_required");
|
|
31
|
+
workspaceRoot = JSON.parse(receipt.context).workspace_root;
|
|
32
|
+
}
|
|
33
|
+
catch (error) {
|
|
34
|
+
if (error instanceof AppError)
|
|
35
|
+
throw error;
|
|
36
|
+
refuse("invalid_start_scope");
|
|
37
|
+
}
|
|
38
|
+
if (wave !== 1 && wave !== 2 || typeof workspaceRoot !== "string" || !workspaceRoot)
|
|
39
|
+
refuse("exact_wave_workspace_required");
|
|
40
|
+
const scope = { projectRoot: run.project_root, runId: work.run_id, workId: work.work_id, workspaceRoot, workSessionId: link.id };
|
|
41
|
+
const restore = () => wave === 1 ? restoreMemoryRepairPublicationFromStage(context, scope) : prepareMemorySecondRepairPublicationFromStage(context, scope);
|
|
42
|
+
// Presence selects only restoration; the real owner admits generation,
|
|
43
|
+
// exact receipt multiplicity and physical scope. Corrupt intents never
|
|
44
|
+
// fall back to a fresh reservation.
|
|
45
|
+
const retained = context.db.get("SELECT 1 FROM audit_events WHERE project_id=? AND event_type='memory.repair_publication_intent' AND json_extract(payload_json,'$.run_id')=? AND json_extract(payload_json,'$.work_id')=? LIMIT 1", [work.project_id, work.run_id, work.work_id]);
|
|
46
|
+
if (retained) {
|
|
47
|
+
const publication = restore();
|
|
48
|
+
if (!publication.bytes().equals(bytes))
|
|
49
|
+
refuse("retained_result_changed");
|
|
50
|
+
return { scope, bytes, wave, publication };
|
|
51
|
+
}
|
|
52
|
+
const outcome = wave === 1 ? prepareMemoryRepairPrivateOutputFromStage(context, { ...scope, bytes }).result().outcome
|
|
53
|
+
: restoreStartedMemorySecondRepairLaunchFromStage(context, scope).observeOutput(context, bytes).result.outcome;
|
|
54
|
+
if (outcome !== "accepted")
|
|
55
|
+
refuse("accepted_private_output_required_use_managed_fail_for_blocker");
|
|
56
|
+
if (wave === 1)
|
|
57
|
+
prepareMemoryRepairPublicationSourceFromStage(context, scope);
|
|
58
|
+
return { scope, bytes, wave, publication: null };
|
|
59
|
+
}
|
|
60
|
+
/** Called by managed Work finish after its normal Hook/actor admission.
|
|
61
|
+
* Preparation is repeated before effects; no caller-supplied object grants
|
|
62
|
+
* authority. Every action is terminal and advances the retained finite cursor. */
|
|
63
|
+
export function publishMemoryRepairWorkCompletion(context, work, result) {
|
|
64
|
+
const prepared = prepareMemoryRepairWorkCompletion(context, work, result);
|
|
65
|
+
const { scope, bytes, wave } = prepared;
|
|
66
|
+
try {
|
|
67
|
+
if (!prepared.publication) {
|
|
68
|
+
if (wave === 1) {
|
|
69
|
+
registerMemoryRepairPublicationSourceFromStage(context, scope);
|
|
70
|
+
reserveMemoryRepairPublicationAgainstSourceFromStage(context, { ...scope, bytes });
|
|
71
|
+
}
|
|
72
|
+
else
|
|
73
|
+
reserveMemorySecondRepairPublicationFromStage(context, { ...scope, bytes });
|
|
74
|
+
}
|
|
75
|
+
const publication = prepared.publication ?? (wave === 1 ? restoreMemoryRepairPublicationFromStage(context, scope) : prepareMemorySecondRepairPublicationFromStage(context, scope));
|
|
76
|
+
const initial = publication.revalidate(context);
|
|
77
|
+
let completed = initial.completed;
|
|
78
|
+
// The actual retained plan, not agent output or a guessed retry count,
|
|
79
|
+
// bounds execution. Even an already applied plan reconciles completion.
|
|
80
|
+
do {
|
|
81
|
+
const step = publication.applyNextAction(context);
|
|
82
|
+
if (step.total !== initial.total || step.completed < completed || step.completed > initial.total)
|
|
83
|
+
refuse("publication_cursor_changed");
|
|
84
|
+
if (step.status === "applied")
|
|
85
|
+
break;
|
|
86
|
+
if (step.completed !== completed + 1)
|
|
87
|
+
refuse("publication_did_not_advance");
|
|
88
|
+
completed = step.completed;
|
|
89
|
+
} while (completed < initial.total);
|
|
90
|
+
return PreparedPublishedMemoryRepairWorkResult.prepare(context, publication);
|
|
91
|
+
}
|
|
92
|
+
catch (error) {
|
|
93
|
+
throw new AppError("memory_repair_completion_pending", "Repair publication requires exact-request reconciliation before another completion attempt", 1, { effect: error instanceof AppError && error.details.effect === "unknown" ? "unknown" : "partial",
|
|
94
|
+
run_id: scope.runId, work_id: scope.workId, work_session_id: scope.workSessionId,
|
|
95
|
+
cause: error instanceof Error ? error.message : String(error), next_action: "Reconcile retained publication and lifecycle invocation; do not start another Session or replace result bytes." });
|
|
96
|
+
}
|
|
97
|
+
}
|
|
@@ -0,0 +1,73 @@
|
|
|
1
|
+
import { AppError } from "../shared/errors.js";
|
|
2
|
+
import { hybridDigest } from "./hybrid-contracts.js";
|
|
3
|
+
import { appendAudit } from "./audit.js";
|
|
4
|
+
import { PreparedRetainedMemoryRepairPublication } from "./memory-repair-publication.js";
|
|
5
|
+
const token = Symbol("published-memory-repair-work-result");
|
|
6
|
+
function refuse(reason) {
|
|
7
|
+
throw new AppError("memory_repair_work_custody_invalid", "Repair settlement requires independently completed publication and its active physical WorkSession", 1, { reason, effect: "no_effect" });
|
|
8
|
+
}
|
|
9
|
+
/** Publication proves file effects, not semantic review. This sealed owner
|
|
10
|
+
* bridges that proof to the existing atomic Work/WorkSession settlement writer.
|
|
11
|
+
* It cannot launch a provider, accept a review finding or close the Stage.
|
|
12
|
+
*/
|
|
13
|
+
export class PreparedPublishedMemoryRepairWorkResult {
|
|
14
|
+
workId;
|
|
15
|
+
sha256;
|
|
16
|
+
#publication;
|
|
17
|
+
#binding;
|
|
18
|
+
constructor(key, publication, binding) {
|
|
19
|
+
if (key !== token)
|
|
20
|
+
refuse("published_result_token_required");
|
|
21
|
+
this.#publication = publication;
|
|
22
|
+
this.#binding = structuredClone(binding);
|
|
23
|
+
this.workId = binding.work_id;
|
|
24
|
+
this.sha256 = binding.result_sha256;
|
|
25
|
+
Object.freeze(this);
|
|
26
|
+
}
|
|
27
|
+
static isPrepared(value) {
|
|
28
|
+
return value instanceof PreparedPublishedMemoryRepairWorkResult && #publication in value;
|
|
29
|
+
}
|
|
30
|
+
static prepare(context, publication) {
|
|
31
|
+
if (!PreparedRetainedMemoryRepairPublication.isPrepared(publication))
|
|
32
|
+
refuse("registered_publication_required");
|
|
33
|
+
publication.revalidateApplied(context);
|
|
34
|
+
const binding = publication.settlementBinding(context);
|
|
35
|
+
const prepared = new PreparedPublishedMemoryRepairWorkResult(token, publication, binding);
|
|
36
|
+
prepared.assertSettlementScope(context, binding.session_id, binding.work_session_id);
|
|
37
|
+
return prepared;
|
|
38
|
+
}
|
|
39
|
+
bytes() { return this.#publication.bytes(); }
|
|
40
|
+
revalidate(context) {
|
|
41
|
+
this.#publication.revalidateApplied(context);
|
|
42
|
+
this.assertSettlementScope(context, this.#binding.session_id, this.#binding.work_session_id);
|
|
43
|
+
}
|
|
44
|
+
/** SQL-only check. The publication owner checks original review/allocation,
|
|
45
|
+
* generation, active Session/provider/CWD, start and completion custody.
|
|
46
|
+
*/
|
|
47
|
+
assertSettlementScope(context, sessionId, workSessionId) {
|
|
48
|
+
if (sessionId !== this.#binding.session_id || workSessionId !== this.#binding.work_session_id
|
|
49
|
+
|| hybridDigest(this.#publication.settlementBinding(context)) !== hybridDigest(this.#binding))
|
|
50
|
+
refuse("repair_settlement_scope_changed");
|
|
51
|
+
}
|
|
52
|
+
recordSettlement(context, sessionId, workSessionId) {
|
|
53
|
+
if (context.db.isOwnedWriteTransaction?.() !== true)
|
|
54
|
+
refuse("owned_settlement_writer_required");
|
|
55
|
+
this.assertSettlementScope(context, sessionId, workSessionId);
|
|
56
|
+
const event = "memory.repair_work_settlement", receipt = { schema_id: "dd-flow/memory-repair-work-settlement@1", ...this.#binding };
|
|
57
|
+
const select = () => context.db.all("SELECT payload_json FROM audit_events WHERE project_id=? AND event_type=? AND json_extract(payload_json,'$.run_id')=? AND json_extract(payload_json,'$.work_id')=? AND json_extract(payload_json,'$.generation')=? LIMIT 2", [this.#binding.project_id, event, this.#binding.run_id, this.workId, this.#binding.generation]);
|
|
58
|
+
if (select().length)
|
|
59
|
+
refuse("repair_settlement_already_recorded");
|
|
60
|
+
appendAudit(context, { projectId: this.#binding.project_id, eventType: event, payload: receipt });
|
|
61
|
+
const rows = select();
|
|
62
|
+
try {
|
|
63
|
+
if (rows.length !== 1 || Buffer.byteLength(rows[0].payload_json) > 16 * 1024
|
|
64
|
+
|| hybridDigest(JSON.parse(rows[0].payload_json)) !== hybridDigest(receipt))
|
|
65
|
+
refuse("repair_settlement_readback_failed");
|
|
66
|
+
}
|
|
67
|
+
catch (error) {
|
|
68
|
+
if (error instanceof AppError)
|
|
69
|
+
throw error;
|
|
70
|
+
refuse("invalid_repair_settlement_readback");
|
|
71
|
+
}
|
|
72
|
+
}
|
|
73
|
+
}
|