feat(confidence-engine): prepare completed episode evidence
This commit is contained in:
@@ -0,0 +1,106 @@
|
||||
/**
|
||||
* Episode preparation — deterministically prepare a completed focused episode
|
||||
* for future authoritative graph reasoning.
|
||||
*
|
||||
* Pure domain transformation:
|
||||
* SituationGraph + targetNodeId + Contributions + Findings → preparedEpisode
|
||||
*
|
||||
* Purity contract:
|
||||
* - No mutation of inputs (situationGraph, contributions, findings)
|
||||
* - No storage I/O
|
||||
* - No fetch / provider / model calls
|
||||
* - No GraphUpdateProposal generation
|
||||
* - No reasoningState modification
|
||||
*/
|
||||
|
||||
// ── Eligibility constants ──────────────────────────────────
|
||||
|
||||
const ELIGIBLE_DISPOSITIONS = new Set([null, "agree"]);
|
||||
|
||||
const EXCLUDED_DISPOSITIONS = new Set(["not_relevant", "not_quite"]);
|
||||
|
||||
// ── Public API ─────────────────────────────────────────────
|
||||
|
||||
/**
|
||||
* Prepare a completed focused episode for deterministic graph reasoning.
|
||||
*
|
||||
* @param {Object} params
|
||||
* @param {Object} params.situationGraph - Authoritative SituationGraph (read-only)
|
||||
* @param {string} params.targetNodeId - The completed target node ID
|
||||
* @param {Array<Object>} params.contributions - All focused Contributions
|
||||
* @param {Array<Object>} params.findings - All canonical Findings
|
||||
* @returns {Object} preparedEpisode
|
||||
*/
|
||||
export function prepareCompletedEpisode({ situationGraph, targetNodeId, contributions, findings }) {
|
||||
// ── 1. Scope contributions to the completed target ───────
|
||||
|
||||
const scopedContributions = [];
|
||||
|
||||
for (const contrib of contributions) {
|
||||
if (contrib?.targetNodeId === targetNodeId) {
|
||||
scopedContributions.push(contrib);
|
||||
}
|
||||
}
|
||||
|
||||
// ── 2. Order deterministically by sequence ───────────────
|
||||
|
||||
const orderedContributions = scopedContributions.slice().sort((a, b) => {
|
||||
const aSeq = a?.sequence != null ? a.sequence : Infinity;
|
||||
const bSeq = b?.sequence != null ? b.sequence : Infinity;
|
||||
return aSeq - bSeq;
|
||||
});
|
||||
|
||||
// ── 3. Build contribution ID lookup for provenance chain ─
|
||||
|
||||
const contribIds = new Set(orderedContributions.map((c) => c.id));
|
||||
|
||||
// ── 4. Prepare turns (ordered Q/A pairs) ────────────────
|
||||
|
||||
const turns = orderedContributions.map((contrib, idx) => ({
|
||||
contributionId: contrib.id ?? null,
|
||||
sequence: contrib.sequence != null ? contrib.sequence : idx + 1,
|
||||
question: contrib.question ?? "",
|
||||
answer: contrib.answer ?? "",
|
||||
}));
|
||||
|
||||
// ── 5. Bucket Findings by eligibility ───────────────────
|
||||
|
||||
const eligibleCanonicalFindings = [];
|
||||
const excludedFindingProvenance = [];
|
||||
|
||||
for (const finding of findings || []) {
|
||||
if (!finding?.contributionId) continue;
|
||||
|
||||
// Membership authority: only Findings whose contributionId references a scoped Contribution
|
||||
if (!contribIds.has(finding.contributionId)) continue;
|
||||
|
||||
const disposition = finding.userDisposition ?? null;
|
||||
|
||||
const bucketedFinding = {
|
||||
findingId: finding.id ?? null,
|
||||
contributionId: finding.contributionId,
|
||||
proposition: finding.proposition ?? "",
|
||||
sourceObservation: finding.sourceObservation ?? "",
|
||||
disposition,
|
||||
};
|
||||
|
||||
if (ELIGIBLE_DISPOSITIONS.has(disposition)) {
|
||||
eligibleCanonicalFindings.push({
|
||||
...bucketedFinding,
|
||||
endorsement: disposition, // null or "agree"
|
||||
});
|
||||
} else if (EXCLUDED_DISPOSITIONS.has(disposition)) {
|
||||
excludedFindingProvenance.push(bucketedFinding);
|
||||
}
|
||||
}
|
||||
|
||||
// ── 6. Compose prepared episode ─────────────────────────
|
||||
|
||||
return {
|
||||
situationGraph: situationGraph ?? null,
|
||||
targetNodeId,
|
||||
turns,
|
||||
eligibleCanonicalFindings,
|
||||
excludedFindingProvenance,
|
||||
};
|
||||
}
|
||||
Reference in New Issue
Block a user