mirror of
https://github.com/morluto/rea.git
synced 2026-11-07 05:56:19 -10:00
refactor(evidence): consolidate import staging
This commit is contained in:
1 parent
c66729eca9
commit
4b6fd03146
2 files changed
+47
-44
No files matched your search
@@ -4,7 +4,10 @@ import canonicalize from "canonicalize";
|
||||
import { describe, expect, it } from "vitest";
|
||||
|
||||
import { EvidenceLedger } from "./EvidenceLedger.js";
|
||||
import { createEvidence } from "../../domain/evidence.js";
|
||||
import {
|
||||
createEvidence,
|
||||
withEvidenceCallerInputPath,
|
||||
} from "../../domain/evidence.js";
|
||||
import {
|
||||
createEvidenceBundle,
|
||||
parseEvidenceBundle,
|
||||
@@ -405,8 +408,20 @@ describe("residual unknown registry resolution and history", () => {
|
||||
const base = ledger();
|
||||
const created = base.recordUnknown(input("Divergent?"), mutation("base"));
|
||||
if (!created.ok) throw created.error;
|
||||
const source = withEvidenceCallerInputPath(
|
||||
evidence("shared-source"),
|
||||
"./original",
|
||||
);
|
||||
expect(base.record(source).ok).toBe(true);
|
||||
const branch = ledger();
|
||||
expect(branch.import(base.export()).ok).toBe(true);
|
||||
expect(
|
||||
branch.import(
|
||||
createEvidenceBundle([
|
||||
withEvidenceCallerInputPath(source, "./relocated"),
|
||||
]),
|
||||
).ok,
|
||||
).toBe(true);
|
||||
expect(
|
||||
base.updateUnknown(update(created.value), mutation("branch-left")).ok,
|
||||
).toBe(true);
|
||||
@@ -418,6 +433,7 @@ describe("residual unknown registry resolution and history", () => {
|
||||
).toBe(true);
|
||||
const before = serializeEvidenceBundle(base.export());
|
||||
expect(base.import(branch.export()).ok).toBe(false);
|
||||
expect(base.get(source.evidence_id)?.caller_input_path).toBe("./original");
|
||||
expect(serializeEvidenceBundle(base.export())).toBe(before);
|
||||
});
|
||||
});
|
||||
|
||||
@@ -100,55 +100,43 @@ export class EvidenceLedger {
|
||||
);
|
||||
}
|
||||
const bundle = parsed.value;
|
||||
// A record-only bundle cannot change the already-valid unknown graph. The
|
||||
// bundle parser has validated every incoming record and its manifests, so
|
||||
// checking ID conflicts and committing only the additions preserves the
|
||||
// atomic merge without rebuilding and reparsing the entire ledger.
|
||||
if (bundle.unknowns.length === 0) {
|
||||
const additions = new Map<string, Evidence>();
|
||||
const replacements: EvidenceMetadataReplacement[] = [];
|
||||
for (const evidence of bundle.records) {
|
||||
const existing = this.#records.get(evidence.evidence_id);
|
||||
if (existing !== undefined) {
|
||||
if (!recordsAgree(existing, evidence))
|
||||
return err(
|
||||
new EvidenceIntegrityError("Conflicting evidence record"),
|
||||
);
|
||||
if (!displayMetadataAgree(existing, evidence)) {
|
||||
replacements.push({
|
||||
kind: "metadata",
|
||||
previous: existing,
|
||||
current: evidence,
|
||||
});
|
||||
}
|
||||
continue;
|
||||
}
|
||||
additions.set(evidence.evidence_id, evidence);
|
||||
}
|
||||
for (const { current } of replacements)
|
||||
this.#records.set(current.evidence_id, current);
|
||||
for (const [id, evidence] of additions) this.#records.set(id, evidence);
|
||||
return ok({
|
||||
recordsAdded: additions.size,
|
||||
unknownsAdded: 0,
|
||||
changed: additions.size > 0 || replacements.length > 0,
|
||||
replacements,
|
||||
});
|
||||
}
|
||||
const pending = new Map(this.#records);
|
||||
// Stage record conflicts and display metadata once, before either commit
|
||||
// path. Unknown history additionally requires validating the merged graph.
|
||||
const recordChanges = new Map<string, Evidence>();
|
||||
let recordsAdded = 0;
|
||||
const replacements: EvidenceMetadataReplacement[] = [];
|
||||
for (const evidence of bundle.records) {
|
||||
const existing = pending.get(evidence.evidence_id);
|
||||
if (existing !== undefined && !recordsAgree(existing, evidence))
|
||||
const existing = this.#records.get(evidence.evidence_id);
|
||||
if (existing === undefined) {
|
||||
recordChanges.set(evidence.evidence_id, evidence);
|
||||
recordsAdded += 1;
|
||||
continue;
|
||||
}
|
||||
if (!recordsAgree(existing, evidence))
|
||||
return err(new EvidenceIntegrityError("Conflicting evidence record"));
|
||||
if (existing !== undefined && !displayMetadataAgree(existing, evidence))
|
||||
if (!displayMetadataAgree(existing, evidence)) {
|
||||
recordChanges.set(evidence.evidence_id, evidence);
|
||||
replacements.push({
|
||||
kind: "metadata",
|
||||
previous: existing,
|
||||
current: evidence,
|
||||
});
|
||||
pending.set(evidence.evidence_id, evidence);
|
||||
}
|
||||
}
|
||||
// A record-only bundle cannot change the already-valid unknown graph.
|
||||
if (bundle.unknowns.length === 0) {
|
||||
for (const evidence of recordChanges.values())
|
||||
this.#records.set(evidence.evidence_id, evidence);
|
||||
return ok({
|
||||
recordsAdded,
|
||||
unknownsAdded: 0,
|
||||
changed: recordChanges.size > 0,
|
||||
replacements,
|
||||
});
|
||||
}
|
||||
const pending = new Map(this.#records);
|
||||
for (const evidence of recordChanges.values())
|
||||
pending.set(evidence.evidence_id, evidence);
|
||||
const pendingUnknowns = new Map(this.#unknownRevisions);
|
||||
let unknownsAdded = 0;
|
||||
for (const unknown of bundle.unknowns) {
|
||||
@@ -170,12 +158,11 @@ export class EvidenceLedger {
|
||||
}),
|
||||
);
|
||||
}
|
||||
const added = pending.size - this.#records.size;
|
||||
this.#commit(checked.value);
|
||||
return ok({
|
||||
recordsAdded: added,
|
||||
recordsAdded,
|
||||
unknownsAdded,
|
||||
changed: added > 0 || unknownsAdded > 0 || replacements.length > 0,
|
||||
changed: recordChanges.size > 0 || unknownsAdded > 0,
|
||||
replacements,
|
||||
});
|
||||
}
|
||||
|
||||
Reference in new issue
Block a user