import { createHash } from "node:crypto"; import type { EvidenceEventV1 } from "../../src/evidence-log.ts"; import { producerFields } from "../../src/instrumentation.ts"; import { RETENTION_TYPE_MAX } from "../../src/retention.ts"; import type { LongTermMemoryEntry, LongTermType } from "../../src/types.ts"; import { TYPES } from "./constants.ts"; import { disappearanceRows } from "./inspection-model.ts"; import { VERSION_ANALYSIS_SAMPLE_THRESHOLD, VERSION_GROUPS, buildVersionBuckets, buildVersionCoverage, computeVersionedInference, hasKnownProducerVersion, hasProducerFields, producerVersionGroupFor, } from "./quality-versioning.ts"; import type { AnswerabilityLevel, ProducerBearingRecord, ProducerVersionGroup, VersionCoverage, VersionedMechanismDiagnosticQuestion, VersionedMechanismFacts, } from "./quality-versioning.ts"; import { hasWorkspaceScope, rejectionQualitySummary, uniqueByCanonicalText } from "./rejections-model.ts"; import { canonicalMemoryText, cleanText, countBy, objectFromCounts, truncate, uniqueStrings, workspaceRootHash } from "./text.ts"; import type { MemoryInspectionReadModel, NormalizedRejection } from "./types.ts"; export type { AnswerabilityLevel, ProducerBearingRecord, ProducerVersionGroup, VersionAvailability, VersionBucketFacts, VersionCoverage, VersionedMechanismDiagnosticQuestion, VersionedMechanismFacts, VersionedMechanismInference, VersionSampleAssessment, } from "./quality-versioning.ts"; export { hasKnownProducerVersion, producerVersionGroupFor } from "./quality-versioning.ts"; export type RejectionVersionFacts = { totalRecords: number; candidateRecords: number; byRawReasonCode: Record; byType: Record; ambiguousOrArchitectureLike: number; }; export type ReinforcementVersionFacts = { reinforceEvents: number; reinforcedEvents: number; rejectedOrBlockedEvents: number; windowBlockedEvents: number; windowBlockRate: number; repeatedBlocksByMemory: Array<{ memoryId: string; count: number; refs: string[]; rawReasonCodes: string[] }>; blocksByExactReason: Record; windowBlocksByUtcDay: Record; blockDetailsMissing: number; malformedCommandEvents: number; }; export type EvictionVersionFacts = { removedByCapacity: number; removedByGlobalCap: number; removedByTypeCap: number; recentEvictionsByType: Record; recentCapacityRemovalsWithSnapshot: number; capacitySnapshotsMissing: number; highestRankRemoved?: { memoryId?: string; rankAtRemoval: number; strengthAtRemoval?: number; type?: string; eventId: string }; }; export type VersionedSystemMechanismFacts = { currentPackageVersion: string; versionCoverage: VersionCoverage; buckets: ProducerVersionGroup[]; sampleThresholds: { rejectionFilters: 5; reinforcementRules: 5; evictionAndCaps: 5; }; rejectionFilters: VersionedMechanismFacts; reinforcementRules: VersionedMechanismFacts; evictionAndCaps: VersionedMechanismFacts; }; export type AnswerabilityAssessment = { level: AnswerabilityLevel; question: string; decision: string; competingExplanations: string[]; requiredSignals: string[]; currentSignals: string[]; outputPermission: string; }; export type AnswerabilityReport = { rejectionFilters: AnswerabilityAssessment; reinforcementRules: AnswerabilityAssessment; evictionAndCaps: AnswerabilityAssessment; unknownDisappearances: AnswerabilityAssessment; identityAndDedup: AnswerabilityAssessment; memoryContent: AnswerabilityAssessment; }; export type ReviewBoardReport = { version: 1; generatedAt: string; workspace: { rootHash: string; key: string }; purpose: "review_evidence_only"; languageGuidance: { nonAuthoritative: true; mutation: "none"; rawReasonCodesAreEvidence: true; producerVersionRecorded: boolean; provenanceInferenceOnly: true; primaryReviewPurpose: "system_mechanism_observations"; secondaryReviewPurpose: "memory_content_quality"; }; provenanceContext: { method: "migration_timestamp_and_format_inference"; confidenceDisclaimer: string; falseCurrentRiskBias: "prefer_unversioned_ambiguous_when_uncertain"; producerVersionAvailable: boolean; migrationTimeline: Array<{ migrationId: string; presentInStore: boolean; firstEvidenceAt?: string }>; lastActivityAt?: string; countsByClassification: Record; candidateLimit?: number; candidateDisplay?: { shown: number; total: number; byMechanism: Record; }; }; facts: { systemMechanisms: { instrumentation: { evidenceEventsTotal: number; evidenceEventsWithProducer: number; rejectionRecordsTotal: number; rejectionRecordsWithProducer: number; instrumentationVersions: Record; }; rejectionFilters: { totalRecords: number; uniqueTexts: number; byRawReasonCode: Record; byType: Record; ambiguousOrArchitectureLike: number; hardReasonOrNoiseHeuristic: number; reabsorbedRejectedTexts: number; }; reinforcementRules: { reinforceEvents: number; reinforcedEvents: number; rejectedOrBlockedEvents: number; windowBlockedEvents: number; windowBlockRate: number; repeatedBlocksByMemory: Array<{ memoryId: string; count: number; refs: string[]; rawReasonCodes: string[] }>; blocksByExactReason: Record; windowBlocksByUtcDay: Record; blockDetailsMissing: number; malformedCommandEvents: number; }; evictionAndCaps: { activeMemories: number; maxEntries: number; renderedMemories: number; typeCounts: Record; typeCaps: Record; fullCaps: string[]; missingEvidenceOnly: number; unknownDisappearances: number; removedByCapacity: number; removedByGlobalCap: number; removedByTypeCap: number; recentEvictionsByType: Record; recentEvictedContentShown: number; recentCapacityRemovalsWithSnapshot: number; capacitySnapshotsMissing: number; highestRankRemoved?: { memoryId?: string; rankAtRemoval: number; strengthAtRemoval?: number; type?: string; eventId: string }; }; identityAndDedup: { replacementEvents: number; sameTypeReplacementEvents: number; crossTypeReplacementEvents: number; supersededEntries: number; duplicateTextOrIdentityGroups: number; }; versionedFacts?: VersionedSystemMechanismFacts; }; memoryContent: { activeMemories: number; renderedMemories: number; evidenceCoverage: { covered: number; total: number }; typeCounts: Record; typeCaps: Record; weakestActiveMemories: Array<{ id: string; type: string; strength?: number; textPreview: string }>; strongestActiveMemories: Array<{ id: string; type: string; strength?: number; textPreview: string }>; }; }; activeMemoryDisplay: { threshold: number; mode: "all" | "sample"; shown: number; total: number; items: ReviewBoardActiveMemory[]; }; reviewCandidates: ReviewBoardCandidate[]; reviewQuestions: { systemMechanism: string[]; memoryContent: string[]; }; answerability?: AnswerabilityReport; nextCommands: string[]; }; export type ReviewBoardActiveMemory = { id: string; type: string; source: string; status: string; strength?: number; text: string; evidence: { eventCount: number; eventIds: string[]; rawReasonCodes: string[] }; provenance?: CandidateProvenance; heuristicFlags: HeuristicFlag[]; reviewQuestions: string[]; }; export type ReviewBoardCandidate = { concernKind: "system_mechanism"; mechanism?: "rejection_filter" | "reinforcement_rule" | "eviction_cap" | "identity_dedup"; source: | "rejection_rule_evidence" | "missing_evidence" | "numbered_command_evidence" | "eviction_cap_evidence" | "reabsorption_evidence" | "identity_dedup_evidence"; id: string; facts: Record; evidence: { eventIds?: string[]; rawReasonCodes?: string[]; textPreview?: string; textAvailable: boolean }; provenance?: CandidateProvenance; versionContext?: { group: ProducerVersionGroup; currentPackageVersion: string; producerVersion?: string; basis: string; }; heuristicFlags: HeuristicFlag[]; reviewQuestions: string[]; nextCommands: string[]; }; export type ProvenanceClassification = | "explicit_migration_evidence" | "legacy_unversioned_format" | "reabsorbed_post_rejection" | "suspected_pre_migration_legacy" | "likely_current_behavior" | "unversioned_ambiguous"; export type CandidateProvenance = { classification: ProvenanceClassification; confidence: "high" | "medium" | "low"; basis: string[]; interpretationCaveat: string; }; export type HeuristicFlag = { id: string; label: string; evidence: string; caveat: string; }; const ACTIVE_MEMORY_FULL_TEXT_THRESHOLD = 40; const REPRESENTATIVE_CANDIDATE_LIMIT = 10; const RECENT_EVICTION_DAYS = 7; const KNOWN_MIGRATION_IDS = [ "2026-04-26-p0-cleanup", "2026-04-28-quality-cleanup", "2026-05-01-retention-clock-backfill", ] as const; const PROVENANCE_CLASSIFICATIONS: ProvenanceClassification[] = [ "explicit_migration_evidence", "legacy_unversioned_format", "reabsorbed_post_rejection", "suspected_pre_migration_legacy", "likely_current_behavior", "unversioned_ambiguous", ]; const HARD_OR_NOISE_REASON_CODES = new Set([ "progress_snapshot", "active_file_snapshot", "commit_or_ci_snapshot", "temporary_status", "raw_error", "code_or_api_signature", "bad_feedback", ]); const MALFORMED_COMMAND_REASON_CODES = new Set([ "invalid_memory_command", "invalid_memory_ref", "invalid_memory_type", "empty_replacement_text", ]); type ReabsorbedMatch = { key: string; record: NormalizedRejection; activeMemory: LongTermMemoryEntry; }; type ProvenanceContextInputs = { firstMigrationBoundary?: string; latestMigrationBoundary?: string; lastActivityAt?: string; }; type DatedCandidateInput = { candidate: ReviewBoardCandidate; timestamp?: string; tieId: string; textHash?: string; }; export function buildQualityReviewBoard( model: MemoryInspectionReadModel, options: { verbose?: boolean; raw?: boolean; json?: boolean; currentProducerVersion?: string }, generatedAt = new Date().toISOString(), ): ReviewBoardReport { const raw = options.raw === true; const currentPackageVersion = options.currentProducerVersion ?? producerFields().producerVersion; const activeMemories = model.store.entries.filter(entry => entry.status !== "superseded"); const typeCounts = typeCountsFor(activeMemories); const typeCaps = Object.fromEntries(TYPES.map(type => [type, RETENTION_TYPE_MAX[type]])); const rejectionSummary = rejectionQualitySummary(model.rejectionRecords); const rejectionGroups = rejectionSummary.possibleFalsePositiveGroups; const migrationTimeline = buildMigrationTimeline(model); const provenanceInputs = migrationBoundaries(model); const activeKeyMatches = reabsorbedRejectedMatches(activeMemories, model.rejectionRecords); const reabsorbedKeys = new Set(activeKeyMatches.map(match => match.key)); const activeMemoryByKey = new Map(activeKeyMatches.map(match => [match.key, match.activeMemory])); const disappearances = disappearanceRows(model); const instrumentationFacts = buildInstrumentationFacts(model.evidenceEvents, model.rejectionRecords); const reinforcementFacts = buildReinforcementFacts(model.evidenceEvents); const evictionFacts = buildEvictionFacts(model, activeMemories, typeCounts, typeCaps, disappearances, generatedAt); const identityFacts = buildIdentityFacts(model, activeMemories); const versionedFacts = buildVersionedSystemMechanismFacts(model.evidenceEvents, model.rejectionRecords, currentPackageVersion, generatedAt); const memoryContentFacts = buildMemoryContentFacts(model, activeMemories, typeCounts, typeCaps, raw); const producerVersionAvailable = [...model.evidenceEvents, ...model.rejectionRecords].some(hasKnownProducerVersion); const systemMechanismCandidateInputs = { rejection_filter: [ ...buildRejectionCandidates(model.rejectionRecords, provenanceInputs, raw, currentPackageVersion), ...buildReabsorptionCandidates(activeKeyMatches, provenanceInputs, raw, currentPackageVersion), ], reinforcement_rule: buildReinforcementCandidates(model.evidenceEvents, provenanceInputs, raw, currentPackageVersion), eviction_cap: buildEvictionCandidates(disappearances, model.evidenceEvents, provenanceInputs, raw, generatedAt, currentPackageVersion), identity_dedup: buildIdentityCandidates(model, activeMemories, provenanceInputs, raw, currentPackageVersion), }; const showAllSystemMechanismCandidates = options.verbose === true || options.json === true; const systemCandidateDisplay = buildSystemCandidateDisplay(systemMechanismCandidateInputs, showAllSystemMechanismCandidates); const allSystemMechanismCandidates = Object.values(systemMechanismCandidateInputs) .flatMap(inputs => selectRepresentative(inputs, true).map(item => item.candidate)); const reviewCandidates = [ ...systemCandidateDisplay.candidates, ]; const activeMemoryDisplay = buildActiveMemoryDisplay(model, activeMemories, reabsorbedKeys, activeMemoryByKey, provenanceInputs, raw, options.verbose === true || options.json === true); const countsByClassification = countProvenanceClassifications(allSystemMechanismCandidates); const answerabilityReport = buildAnswerabilityReport(); applyInstrumentedAnswerability(answerabilityReport, model.evidenceEvents, reinforcementFacts, evictionFacts, currentPackageVersion); const report: ReviewBoardReport = { version: 1, generatedAt, workspace: { rootHash: workspaceRootHash(model.snapshot?.store?.workspace?.root ?? model.store.workspace.root), key: model.snapshot?.store?.workspace?.key ?? model.store.workspace.key, }, purpose: "review_evidence_only", languageGuidance: { nonAuthoritative: true, mutation: "none", rawReasonCodesAreEvidence: true, producerVersionRecorded: producerVersionAvailable, provenanceInferenceOnly: true, primaryReviewPurpose: "system_mechanism_observations", secondaryReviewPurpose: "memory_content_quality", }, provenanceContext: { method: "migration_timestamp_and_format_inference", confidenceDisclaimer: "Producer version is not recorded in historical evidence; provenance is inferred and should not be used as proof of current behavior.", falseCurrentRiskBias: "prefer_unversioned_ambiguous_when_uncertain", producerVersionAvailable, migrationTimeline, lastActivityAt: model.store.lastActivityAt, countsByClassification, ...(systemCandidateDisplay.limited ? { candidateLimit: REPRESENTATIVE_CANDIDATE_LIMIT, candidateDisplay: systemCandidateDisplay.summary } : {}), }, facts: { systemMechanisms: { instrumentation: instrumentationFacts, rejectionFilters: { totalRecords: rejectionSummary.totalRecords, uniqueTexts: rejectionSummary.uniqueTexts, byRawReasonCode: rejectionSummary.reasonDistribution, byType: objectFromCounts(countBy(model.rejectionRecords.map(record => record.type))), ambiguousOrArchitectureLike: (rejectionGroups.architecture_like_possible_false_positive?.count ?? 0) + (rejectionGroups.ambiguous?.count ?? 0), hardReasonOrNoiseHeuristic: rejectionGroups.clearly_garbage?.count ?? 0, reabsorbedRejectedTexts: new Set(activeKeyMatches.map(match => match.key)).size, }, reinforcementRules: reinforcementFacts, evictionAndCaps: evictionFacts, identityAndDedup: identityFacts, versionedFacts, }, memoryContent: memoryContentFacts, }, activeMemoryDisplay, reviewCandidates, reviewQuestions: { systemMechanism: systemMechanismQuestions(), memoryContent: memoryContentQuestions(), }, nextCommands: nextCommands(), }; report.answerability = answerabilityReport; return report; } function buildAnswerabilityReport(): AnswerabilityReport { return { rejectionFilters: { level: "partial", question: "Are durable memories being rejected?", decision: "Tune rejection filter or leave unchanged", competingExplanations: [ "Rejection filter correctly identifies non-durable candidates", "Rejection filter over-filters durable architectural decisions", ], requiredSignals: ["full rejected text", "reason codes", "source/origin", "decision logic version", "later reabsorption"], currentSignals: ["redacted text preview", "reason code distribution", "type distribution"], outputPermission: "Show candidates and ask for review; do not claim false positives.", }, reinforcementRules: { level: "inventory_only", question: "Is the reinforcement window appropriate?", decision: "Change reinforcement policy or leave unchanged", competingExplanations: [ "Window blocks same-day repeated command inventory", "Window blocks legitimate recurring reinforcement across days", ], requiredSignals: ["per-attempt timestamp", "prior reinforcement timestamp", "exact block reason", "session/day grouping"], currentSignals: ["block count", "repeated block groups", "window block rate"], outputPermission: "Show counts and event IDs only; do not characterize the policy as strict or lenient.", }, evictionAndCaps: { level: "inventory_only", question: "Is capacity causing important memory loss?", decision: "Raise caps or leave unchanged", competingExplanations: [ "Full caps are naturally occupied with healthy turnover", "Full caps are causing premature eviction of important memories", ], requiredSignals: ["strength/rank at removal", "cutoff context", "age", "source", "reinforcement count"], currentSignals: ["removal count", "cap occupancy", "type counts"], outputPermission: "Show occupancy and removal counts only; do not infer a capacity problem from full caps.", }, unknownDisappearances: { level: "inventory_only", question: "Are missing memories current instrumentation defects?", decision: "Fix removal logging or accept historical gaps", competingExplanations: [ "Missing memories were removed before terminal-removal instrumentation existed", "Missing memories indicate a current data-loss defect", ], requiredSignals: ["producer/instrumentation version", "terminal-removal instrumentation epoch", "latest event timestamp"], currentSignals: ["evidence absence", "latest known event timestamp"], outputPermission: "Call them unversioned/ambiguous disappearance inventory unless producer data proves current instrumentation.", }, identityAndDedup: { level: "partial", question: "Are separate concepts incorrectly merged?", decision: "Review identity rules or accept current behavior", competingExplanations: [ "Replacements correctly update semantic duplicates", "Replacements incorrectly collapse distinct concepts with similar text", ], requiredSignals: ["before/after content", "identity keys", "replacement reason"], currentSignals: ["replacement events", "superseded entries"], outputPermission: "Show replacement/duplicate inventory; do not infer semantic correctness.", }, memoryContent: { level: "partial", question: "Are active memories useful and current?", decision: "Review active memories or leave unchanged", competingExplanations: [ "Low text preview indicates a naturally ephemeral memory", "Low text preview indicates potentially stale content", ], requiredSignals: ["full text", "type", "source", "age", "evidence coverage"], currentSignals: ["text preview", "type distribution", "strength distribution"], outputPermission: "Show review surface with text previews; invariant heuristic flags only.", }, }; } function applyInstrumentedAnswerability( report: AnswerabilityReport, events: EvidenceEventV1[], reinforcementFacts: ReviewBoardReport["facts"]["systemMechanisms"]["reinforcementRules"], evictionFacts: ReviewBoardReport["facts"]["systemMechanisms"]["evictionAndCaps"], currentPackageVersion: string, ): void { const hasInstrumentedBlocks = events.some(event => isReinforcementEvent(event) && event.reasonCodes.includes("reinforcement_window_blocked") && typeof event.details?.blockReason === "string" && hasProducerFields(event) && producerVersionGroupFor(event, currentPackageVersion) === "current" ); if (hasInstrumentedBlocks && Object.keys(reinforcementFacts.blocksByExactReason).length > 0) { report.reinforcementRules.level = "partial"; report.reinforcementRules.currentSignals = uniqueStrings([ ...report.reinforcementRules.currentSignals, "exact block reasons", "elapsed-window details when present", ]); report.reinforcementRules.outputPermission = "Show exact block reasons and elapsed-window details when present; causal fields exist but human content judgment is still required."; } const hasCapacitySnapshots = evictionFacts.recentCapacityRemovalsWithSnapshot > 0 && events.some(event => event.type === "memory_removed_capacity" && hasProducerFields(event) && hasCapacitySnapshot(event) && producerVersionGroupFor(event, currentPackageVersion) === "current"); if (hasCapacitySnapshots) { report.evictionAndCaps.level = "partial"; report.evictionAndCaps.currentSignals = uniqueStrings([ ...report.evictionAndCaps.currentSignals, "rank at removal", "strength at removal", ]); report.evictionAndCaps.outputPermission = "Show removal snapshots with rank/strength/age; causal fields exist but human judgment of importance is still required."; } } function buildInstrumentationFacts( events: EvidenceEventV1[], rejections: NormalizedRejection[], ): ReviewBoardReport["facts"]["systemMechanisms"]["instrumentation"] { const instrumentedEvents = events.filter(hasProducerFields); const instrumentedRejections = rejections.filter(hasProducerFields); const instrumentationVersions: Record = {}; for (const record of [...instrumentedEvents, ...instrumentedRejections]) { const version = record.instrumentationVersion; if (typeof version !== "number") continue; const key = String(version); instrumentationVersions[key] = (instrumentationVersions[key] ?? 0) + 1; } return { evidenceEventsTotal: events.length, evidenceEventsWithProducer: instrumentedEvents.length, rejectionRecordsTotal: rejections.length, rejectionRecordsWithProducer: instrumentedRejections.length, instrumentationVersions, }; } function buildVersionedSystemMechanismFacts( events: EvidenceEventV1[], rejections: NormalizedRejection[], currentPackageVersion: string, generatedAt: string, ): VersionedSystemMechanismFacts { const versionCoverage = buildVersionCoverage([...events, ...rejections], currentPackageVersion); return { currentPackageVersion, versionCoverage, buckets: VERSION_GROUPS, sampleThresholds: { rejectionFilters: VERSION_ANALYSIS_SAMPLE_THRESHOLD, reinforcementRules: VERSION_ANALYSIS_SAMPLE_THRESHOLD, evictionAndCaps: VERSION_ANALYSIS_SAMPLE_THRESHOLD, }, rejectionFilters: buildVersionedRejectionFacts(rejections, currentPackageVersion), reinforcementRules: buildVersionedReinforcementFacts(events, currentPackageVersion), evictionAndCaps: buildVersionedEvictionFacts(events, currentPackageVersion, generatedAt), }; } function buildVersionedRejectionFacts( records: NormalizedRejection[], currentPackageVersion: string, ): VersionedMechanismFacts { const buckets = buildVersionBuckets(records, currentPackageVersion, bucketRecords => { const candidateRecords = bucketRecords.filter(isReviewableRejectionCandidate); const facts: RejectionVersionFacts = { totalRecords: bucketRecords.length, candidateRecords: candidateRecords.length, byRawReasonCode: objectFromCounts(countBy(bucketRecords.flatMap(record => record.reasons))), byType: objectFromCounts(countBy(bucketRecords.map(record => record.type))), ambiguousOrArchitectureLike: candidateRecords.length, }; return { facts, opportunityCount: candidateRecords.length, observedPatternCount: candidateRecords.length }; }); const base: Omit, "inference"> = { currentPackageVersion, opportunityName: "rejection candidates", sampleThreshold: VERSION_ANALYSIS_SAMPLE_THRESHOLD, buckets, }; return { ...base, inference: computeVersionedInference(base, { observedPattern: "rejected candidates", patternName: "reviewable_rejection_candidate", }), }; } function buildVersionedReinforcementFacts( events: EvidenceEventV1[], currentPackageVersion: string, ): VersionedMechanismFacts { const mechanismEvents = events.filter(event => isReinforcementEvent(event) || isMalformedCommandEvent(event)); const buckets = buildVersionBuckets(mechanismEvents, currentPackageVersion, bucketEvents => { const attempts = bucketEvents.filter(isReinforcementEvent); const facts = buildReinforcementFacts(bucketEvents) as ReinforcementVersionFacts; return { facts, opportunityCount: attempts.length, observedPatternCount: attempts.filter(event => event.reasonCodes.includes("reinforcement_window_blocked")).length, }; }); const base: Omit, "inference"> = { currentPackageVersion, opportunityName: "attempts", sampleThreshold: VERSION_ANALYSIS_SAMPLE_THRESHOLD, buckets, }; return { ...base, inference: computeVersionedInference(base, { observedPattern: "blocked", patternName: "reinforcement_window_blocked", }), ...diagnosticQuestionsProperty(buildReinforcementDiagnosticQuestions(mechanismEvents, currentPackageVersion)), }; } function diagnosticQuestionsProperty(questions: VersionedMechanismDiagnosticQuestion[]): { diagnosticQuestions?: VersionedMechanismDiagnosticQuestion[] } { return questions.length > 0 ? { diagnosticQuestions: questions } : {}; } function buildReinforcementDiagnosticQuestions(events: EvidenceEventV1[], currentPackageVersion: string): VersionedMechanismDiagnosticQuestion[] { const matching = events .filter(event => isReinforcementEvent(event) && event.reasonCodes.includes("reinforcement_window_blocked") && producerVersionGroupFor(event, currentPackageVersion) === "current" && event.details?.blockReason === "same_session") .map(event => { const attemptedAtIso = stringDetail(event, "attemptedAtIso"); const lastReinforcedAtIso = stringDetail(event, "lastReinforcedAtIso"); return { event, attemptedAtIso, lastReinforcedAtIso }; }) .filter((item): item is { event: EvidenceEventV1; attemptedAtIso: string; lastReinforcedAtIso: string } => typeof item.attemptedAtIso === "string" && typeof item.lastReinforcedAtIso === "string" && isValidIsoDate(item.attemptedAtIso) && isValidIsoDate(item.lastReinforcedAtIso) && item.attemptedAtIso.slice(0, 10) !== item.lastReinforcedAtIso.slice(0, 10) ) .sort((a, b) => compareIso(b.attemptedAtIso, a.attemptedAtIso) || a.event.eventId.localeCompare(b.event.eventId)); const representative = matching[0]; if (!representative) return []; return [{ mechanism: "reinforcement_rule", group: "current", question: "Should same_session reinforcement blocking apply across UTC days?", evidence: [ `count=${matching.length}`, `eventId=${representative.event.eventId}`, `attemptedAtIso=${representative.attemptedAtIso}`, `lastReinforcedAtIso=${representative.lastReinforcedAtIso}`, ], }]; } function buildVersionedEvictionFacts( events: EvidenceEventV1[], currentPackageVersion: string, generatedAt: string, ): VersionedMechanismFacts { const capacityEvents = events.filter(event => event.type === "memory_removed_capacity"); const buckets = buildVersionBuckets(capacityEvents, currentPackageVersion, bucketEvents => { const facts = buildEvictionVersionFacts(bucketEvents, generatedAt); return { facts, opportunityCount: bucketEvents.length, observedPatternCount: facts.capacitySnapshotsMissing, }; }); const base: Omit, "inference"> = { currentPackageVersion, opportunityName: "capacity removals", sampleThreshold: VERSION_ANALYSIS_SAMPLE_THRESHOLD, buckets, }; return { ...base, inference: computeVersionedInference(base, { observedPattern: "missing snapshots", patternName: "capacity_snapshot_missing", }), }; } function buildEvictionVersionFacts(capacityEvents: EvidenceEventV1[], generatedAt: string): EvictionVersionFacts { const recentCapacityEvents = capacityEvents.filter(event => isWithinDaysOf(event.createdAt, generatedAt, RECENT_EVICTION_DAYS)); const capacityEventsWithSnapshot = capacityEvents.filter(hasCapacitySnapshot); const capacityEventsWithRank = capacityEvents.filter(event => numberDetail(event, "rankAtRemoval") !== undefined); const highestRankRemovedEvent = [...capacityEventsWithRank] .sort((a, b) => (numberDetail(a, "rankAtRemoval") ?? Number.POSITIVE_INFINITY) - (numberDetail(b, "rankAtRemoval") ?? Number.POSITIVE_INFINITY))[0]; return { removedByCapacity: capacityEvents.length, removedByGlobalCap: capacityEvents.filter(event => event.reasonCodes.includes("global_cap")).length, removedByTypeCap: capacityEvents.filter(event => event.reasonCodes.includes("type_cap")).length, recentEvictionsByType: objectFromCounts(countBy(recentCapacityEvents.map(event => event.memory?.type ?? "unknown"))), recentCapacityRemovalsWithSnapshot: capacityEventsWithSnapshot.length, capacitySnapshotsMissing: capacityEvents.length - capacityEventsWithSnapshot.length, ...(highestRankRemovedEvent ? { highestRankRemoved: highestRankRemoved(highestRankRemovedEvent) } : {}), }; } function isReviewableRejectionCandidate(record: NormalizedRejection): boolean { if (!record.reasons.includes("bad_decision")) return false; const label = neutralRejectionLabel(record); return label === "architecture_like_rejected_candidate" || label === "ambiguous_rejected_candidate"; } function typeCountsFor(entries: LongTermMemoryEntry[]): Record { return Object.fromEntries(TYPES.map(type => [type, entries.filter(entry => entry.type === type).length])); } function buildMigrationTimeline(model: MemoryInspectionReadModel): ReviewBoardReport["provenanceContext"]["migrationTimeline"] { const present = new Set(model.store.migrations ?? []); return KNOWN_MIGRATION_IDS.map(migrationId => { const matchingTimes = model.evidenceEvents .filter(event => event.details?.migrationId === migrationId) .map(event => event.createdAt) .sort(); const row: { migrationId: string; presentInStore: boolean; firstEvidenceAt?: string } = { migrationId, presentInStore: present.has(migrationId), }; if (matchingTimes[0]) row.firstEvidenceAt = matchingTimes[0]; return row; }); } function migrationBoundaries(model: MemoryInspectionReadModel): ProvenanceContextInputs { const present = new Set(model.store.migrations ?? []); const matchingTimes = model.evidenceEvents .filter(event => typeof event.details?.migrationId === "string" && present.has(event.details.migrationId)) .map(event => event.createdAt) .sort(); return { firstMigrationBoundary: matchingTimes[0], latestMigrationBoundary: matchingTimes[matchingTimes.length - 1], lastActivityAt: model.store.lastActivityAt, }; } function classifyProvenance(input: { event?: EvidenceEventV1; rejection?: NormalizedRejection; reabsorbed?: boolean; }, context: ProvenanceContextInputs): CandidateProvenance { if (input.event?.details?.migrationId) { return provenance("explicit_migration_evidence", "high", [`migration evidence event ${String(input.event.details.migrationId)}`]); } if (input.rejection && !hasWorkspaceScope(input.rejection)) { return provenance("legacy_unversioned_format", "high", ["rejection record without workspace scope fields"]); } if (input.reabsorbed) { return provenance("reabsorbed_post_rejection", "high", ["typed canonical rejected text appears in active memory"]); } const timestamp = input.event?.createdAt ?? input.rejection?.timestamp; if (timestamp && context.firstMigrationBoundary && compareIso(timestamp, context.firstMigrationBoundary) < 0) { return provenance("suspected_pre_migration_legacy", "medium", ["evidence timestamp predates first known migration boundary"]); } if (timestamp && context.latestMigrationBoundary && compareIso(timestamp, context.latestMigrationBoundary) >= 0) { if (!context.lastActivityAt || compareIso(timestamp, context.lastActivityAt) >= 0) { return provenance("likely_current_behavior", "medium", ["evidence timestamp is after known migration evidence and workspace last activity"]); } } return provenance("unversioned_ambiguous", "low", ["no producer version or decisive migration/timestamp signal is recorded"]); } function provenance(classification: ProvenanceClassification, confidence: CandidateProvenance["confidence"], basis: string[]): CandidateProvenance { return { classification, confidence, basis, interpretationCaveat: "Producer version is not recorded; treat this as inferred review context rather than proof of current behavior.", }; } function versionContextFor(record: ProducerBearingRecord | undefined, currentPackageVersion: string): ReviewBoardCandidate["versionContext"] | undefined { if (!record) return undefined; const group = producerVersionGroupFor(record, currentPackageVersion); const producerVersion = typeof record.producerVersion === "string" ? record.producerVersion.trim() : undefined; return { group, currentPackageVersion, ...(producerVersion ? { producerVersion } : {}), basis: group === "current" ? "producerVersion matches current package version" : group === "previous" ? "producerVersion differs from current package version" : "producerVersion is missing, unknown, or empty", }; } function compareIso(a: string, b: string): number { const aTime = new Date(a).getTime(); const bTime = new Date(b).getTime(); if (!Number.isFinite(aTime) || !Number.isFinite(bTime)) return 0; return aTime - bTime; } function reabsorbedRejectedMatches(activeMemories: LongTermMemoryEntry[], records: NormalizedRejection[]): ReabsorbedMatch[] { const activeByKey = new Map(activeMemories.map(memory => [typedCanonicalKey(memory.type, memory.text), memory])); const matches: ReabsorbedMatch[] = []; const seen = new Set(); for (const record of records) { const key = typedCanonicalKey(record.type, record.text); const activeMemory = activeByKey.get(key); if (!activeMemory || seen.has(key)) continue; seen.add(key); matches.push({ key, record, activeMemory }); } return matches; } function typedCanonicalKey(type: LongTermType | string, text: string): string { return `${type}:${canonicalMemoryText(text)}`; } function buildReinforcementFacts(events: EvidenceEventV1[]): ReviewBoardReport["facts"]["systemMechanisms"]["reinforcementRules"] { const attempts = events.filter(isReinforcementEvent); const windowBlocked = attempts.filter(event => event.reasonCodes.includes("reinforcement_window_blocked")); const grouped = new Map; rawReasonCodes: Set; eventIds: string[] }>(); const blocksByExactReason: Record = {}; const windowBlocksByUtcDay: Record = {}; let blockDetailsMissing = 0; for (const event of windowBlocked) { const memoryId = event.memory?.memoryId ?? "unknown"; const current = grouped.get(memoryId) ?? { memoryId, count: 0, refs: new Set(), rawReasonCodes: new Set(), eventIds: [] }; current.count += 1; current.eventIds.push(event.eventId); const ref = typeof event.details?.ref === "string" ? event.details.ref : undefined; if (ref) current.refs.add(ref); for (const reason of event.reasonCodes) current.rawReasonCodes.add(reason); grouped.set(memoryId, current); const blockReason = typeof event.details?.blockReason === "string" ? event.details.blockReason : undefined; if (blockReason) { blocksByExactReason[blockReason] = (blocksByExactReason[blockReason] ?? 0) + 1; const utcDay = event.createdAt?.slice(0, 10) || "unknown"; windowBlocksByUtcDay[utcDay] = (windowBlocksByUtcDay[utcDay] ?? 0) + 1; } else { blockDetailsMissing += 1; } } return { reinforceEvents: attempts.length, reinforcedEvents: attempts.filter(event => event.outcome === "reinforced" || event.type === "memory_reinforced" && event.outcome !== "rejected").length, rejectedOrBlockedEvents: attempts.filter(event => event.outcome === "rejected" || event.reasonCodes.includes("reinforcement_window_blocked")).length, windowBlockedEvents: windowBlocked.length, windowBlockRate: attempts.length === 0 ? 0 : windowBlocked.length / attempts.length, repeatedBlocksByMemory: [...grouped.values()] .filter(group => group.count > 1) .sort((a, b) => b.count - a.count || a.memoryId.localeCompare(b.memoryId)) .map(group => ({ memoryId: group.memoryId, count: group.count, refs: [...group.refs].sort(), rawReasonCodes: [...group.rawReasonCodes].sort() })), blocksByExactReason, windowBlocksByUtcDay, blockDetailsMissing, malformedCommandEvents: events.filter(isMalformedCommandEvent).length, }; } function isReinforcementEvent(event: EvidenceEventV1): boolean { const type = String(event.type); return type === "memory_reinforced" || type === "reinforce_memory" || type === "reinforced" || event.phase === "reinforcement"; } function isMalformedCommandEvent(event: EvidenceEventV1): boolean { return event.type === "extraction_candidate_rejected" && event.reasonCodes.some(reason => MALFORMED_COMMAND_REASON_CODES.has(reason)); } function buildEvictionFacts( model: MemoryInspectionReadModel, activeMemories: LongTermMemoryEntry[], typeCounts: Record, typeCaps: Record, disappearances: ReturnType, generatedAt: string, ): ReviewBoardReport["facts"]["systemMechanisms"]["evictionAndCaps"] { const capacityEvents = model.evidenceEvents.filter(event => event.type === "memory_removed_capacity"); const recentCapacityEvents = capacityEvents.filter(event => isWithinDaysOf(event.createdAt, generatedAt, RECENT_EVICTION_DAYS)); const capacityEventsWithSnapshot = capacityEvents.filter(hasCapacitySnapshot); const capacityEventsWithRank = capacityEvents.filter(event => numberDetail(event, "rankAtRemoval") !== undefined); const highestRankRemovedEvent = [...capacityEventsWithRank] .sort((a, b) => (numberDetail(a, "rankAtRemoval") ?? Number.POSITIVE_INFINITY) - (numberDetail(b, "rankAtRemoval") ?? Number.POSITIVE_INFINITY))[0]; const fullCaps = [ ...(activeMemories.length >= model.store.limits.maxEntries ? ["global"] : []), ...TYPES.filter(type => (typeCounts[type] ?? 0) >= (typeCaps[type] ?? Number.POSITIVE_INFINITY)), ]; return { activeMemories: activeMemories.length, maxEntries: model.store.limits.maxEntries, renderedMemories: model.snapshot.retention.rendered.length, typeCounts, typeCaps, fullCaps, missingEvidenceOnly: disappearances.length, unknownDisappearances: disappearances.filter(row => row.classification === "historical_absent_unknown_reason").length, removedByCapacity: capacityEvents.length, removedByGlobalCap: capacityEvents.filter(event => event.reasonCodes.includes("global_cap")).length, removedByTypeCap: capacityEvents.filter(event => event.reasonCodes.includes("type_cap")).length, recentEvictionsByType: objectFromCounts(countBy(recentCapacityEvents.map(event => event.memory?.type ?? "unknown"))), recentEvictedContentShown: recentCapacityEvents.length, recentCapacityRemovalsWithSnapshot: capacityEventsWithSnapshot.length, capacitySnapshotsMissing: capacityEvents.length - capacityEventsWithSnapshot.length, ...(highestRankRemovedEvent ? { highestRankRemoved: highestRankRemoved(highestRankRemovedEvent) } : {}), }; } function hasCapacitySnapshot(event: EvidenceEventV1): boolean { return event.type === "memory_removed_capacity" && numberDetail(event, "strengthAtRemoval") !== undefined && numberDetail(event, "rankAtRemoval") !== undefined; } function highestRankRemoved(event: EvidenceEventV1): NonNullable { const rankAtRemoval = numberDetail(event, "rankAtRemoval") ?? Number.POSITIVE_INFINITY; const strengthAtRemoval = numberDetail(event, "strengthAtRemoval"); return { ...(event.memory?.memoryId ? { memoryId: event.memory.memoryId } : {}), rankAtRemoval, ...(strengthAtRemoval !== undefined ? { strengthAtRemoval } : {}), ...(event.memory?.type ? { type: event.memory.type } : {}), eventId: event.eventId, }; } function numberDetail(event: EvidenceEventV1, key: string): number | undefined { const value = event.details?.[key]; return typeof value === "number" && Number.isFinite(value) ? value : undefined; } function stringDetail(event: EvidenceEventV1, key: string): string | undefined { const value = event.details?.[key]; return typeof value === "string" && value.trim().length > 0 ? value : undefined; } function isValidIsoDate(value: string): boolean { return Number.isFinite(new Date(value).getTime()) && /^\d{4}-\d{2}-\d{2}/.test(value); } function isWithinDaysOf(iso: string, referenceIso: string, days: number): boolean { const time = new Date(iso).getTime(); const reference = new Date(referenceIso).getTime(); return Number.isFinite(time) && Number.isFinite(reference) && time >= reference - days * 86_400_000; } function buildIdentityFacts(model: MemoryInspectionReadModel, activeMemories: LongTermMemoryEntry[]): ReviewBoardReport["facts"]["systemMechanisms"]["identityAndDedup"] { const replacementEvents = model.evidenceEvents.filter(event => event.type === "memory_replaced_numbered_ref"); return { replacementEvents: replacementEvents.length, sameTypeReplacementEvents: replacementEvents.filter(isSameTypeReplacement).length, crossTypeReplacementEvents: replacementEvents.filter(isCrossTypeReplacement).length, supersededEntries: model.store.entries.filter(entry => entry.status === "superseded").length, duplicateTextOrIdentityGroups: duplicateGroups(activeMemories, model.evidenceEvents).length, }; } function isSameTypeReplacement(event: EvidenceEventV1): boolean { if (event.reasonCodes.includes("same_type_replace")) return true; const types = relationTypes(event); return types.length >= 2 && new Set(types).size === 1; } function isCrossTypeReplacement(event: EvidenceEventV1): boolean { if (event.reasonCodes.includes("cross_type_replace")) return true; const types = relationTypes(event); return types.length >= 2 && new Set(types).size > 1; } function relationTypes(event: EvidenceEventV1): string[] { return uniqueStrings(event.relations?.map(relation => relation.memory?.type ?? "") ?? []); } function duplicateGroups(activeMemories: LongTermMemoryEntry[], events: EvidenceEventV1[]): Array<{ id: string; memoryIds: string[]; basis: string }> { const groups: Array<{ id: string; memoryIds: string[]; basis: string }> = []; const byText = groupBy(activeMemories, memory => typedCanonicalKey(memory.type, memory.text)); for (const [key, memories] of byText.entries()) { if (memories.length > 1) groups.push({ id: `text:${hashText(key)}`, memoryIds: memories.map(memory => memory.id).sort(), basis: "exact typed canonical text" }); } const identityRefs = events .map(event => event.memory) .filter((memory): memory is NonNullable => Boolean(memory?.identityKeyHash && memory.memoryId)); const byIdentity = groupBy(identityRefs, memory => String(memory.identityKeyHash)); for (const [key, refs] of byIdentity.entries()) { const ids = uniqueStrings(refs.map(ref => ref.memoryId ?? "")).sort(); if (ids.length > 1) groups.push({ id: `identity:${hashText(key)}`, memoryIds: ids, basis: "shared evidence identity hash" }); } return groups; } function groupBy(items: T[], keyFor: (item: T) => string): Map { const grouped = new Map(); for (const item of items) { const key = keyFor(item); grouped.set(key, [...(grouped.get(key) ?? []), item]); } return grouped; } function buildMemoryContentFacts( model: MemoryInspectionReadModel, activeMemories: LongTermMemoryEntry[], typeCounts: Record, typeCaps: Record, raw: boolean, ): ReviewBoardReport["facts"]["memoryContent"] { const evidenceCovered = activeMemories.filter(memory => (model.evidenceByMemoryId.get(memory.id) ?? []).length > 0).length; const weakest = model.snapshot.retention.sorted.slice(-5).reverse(); const strongest = model.snapshot.retention.sorted.slice(0, 5); return { activeMemories: activeMemories.length, renderedMemories: model.snapshot.retention.rendered.length, evidenceCoverage: { covered: evidenceCovered, total: activeMemories.length }, typeCounts, typeCaps, weakestActiveMemories: weakest.map(item => retentionPreview(item.entry, item.strength, raw)), strongestActiveMemories: strongest.map(item => retentionPreview(item.entry, item.strength, raw)), }; } function retentionPreview(entry: LongTermMemoryEntry, strength: number | undefined, raw: boolean): { id: string; type: string; strength?: number; textPreview: string } { return { id: entry.id, type: entry.type, strength, textPreview: truncate(cleanText(entry.text, raw), 120) }; } function buildActiveMemoryDisplay( model: MemoryInspectionReadModel, activeMemories: LongTermMemoryEntry[], reabsorbedKeys: Set, activeMemoryByKey: Map, provenanceInputs: ProvenanceContextInputs, raw: boolean, verbose: boolean, ): ReviewBoardReport["activeMemoryDisplay"] { const mode: "all" | "sample" = activeMemories.length <= ACTIVE_MEMORY_FULL_TEXT_THRESHOLD || verbose ? "all" : "sample"; const shownMemories = mode === "all" ? activeMemories : activeMemories.slice(0, ACTIVE_MEMORY_FULL_TEXT_THRESHOLD); const items = shownMemories.map(memory => { const events = model.evidenceByMemoryId.get(memory.id) ?? []; const key = typedCanonicalKey(memory.type, memory.text); const item: ReviewBoardActiveMemory = { id: memory.id, type: memory.type, source: memory.source, status: memory.status, strength: model.snapshot.retention.sorted.find(candidate => candidate.entry.id === memory.id)?.strength, text: cleanText(memory.text, raw), evidence: { eventCount: events.length, eventIds: events.map(event => event.eventId), rawReasonCodes: uniqueStrings(events.flatMap(event => event.reasonCodes)).sort(), }, heuristicFlags: activeMemoryFlags(memory, events), reviewQuestions: memoryContentQuestions(), }; if (reabsorbedKeys.has(key) && activeMemoryByKey.get(key)?.id === memory.id) { item.provenance = classifyProvenance({ reabsorbed: true }, provenanceInputs); } return item; }); return { threshold: ACTIVE_MEMORY_FULL_TEXT_THRESHOLD, mode, shown: items.length, total: activeMemories.length, items }; } function activeMemoryFlags(memory: LongTermMemoryEntry, events: EvidenceEventV1[]): HeuristicFlag[] { const flags: HeuristicFlag[] = []; if (events.length === 0) { flags.push(flag("no_evidence", "No linked evidence events", `memory ${memory.id} has no lifecycle evidence events`)); } if ((memory.supersedes ?? []).length > 0) { flags.push(flag("supersedes_other_memory", "Supersession relationship present", `memory ${memory.id} supersedes ${memory.supersedes?.length ?? 0} prior entries`)); } return flags; } function buildSystemCandidateDisplay( candidateInputs: Record, showAll: boolean, ): { candidates: ReviewBoardCandidate[]; limited: boolean; summary: NonNullable } { const candidates: ReviewBoardCandidate[] = []; const byMechanism: Record = {}; let shown = 0; let total = 0; let limited = false; for (const [mechanism, inputs] of Object.entries(candidateInputs)) { const selected = selectRepresentative(inputs, showAll); candidates.push(...selected.map(item => item.candidate)); byMechanism[mechanism] = { shown: selected.length, total: inputs.length }; shown += selected.length; total += inputs.length; if (selected.length < inputs.length) limited = true; } return { candidates, limited, summary: { shown, total, byMechanism } }; } function buildRejectionCandidates(records: NormalizedRejection[], context: ProvenanceContextInputs, raw: boolean, currentPackageVersion: string): DatedCandidateInput[] { const candidateRecords = records .filter(record => record.reasons.includes("bad_decision")) .map(record => ({ record, label: neutralRejectionLabel(record) })) .filter(item => item.label === "architecture_like_rejected_candidate" || item.label === "ambiguous_rejected_candidate") .sort((a, b) => timestampValue(b.record.timestamp) - timestampValue(a.record.timestamp) || a.record.type.localeCompare(b.record.type) || a.record.text.localeCompare(b.record.text) ); const candidates = uniqueByCanonicalText(candidateRecords.map(item => item.record)) .map(record => ({ record, label: neutralRejectionLabel(record) })) .map(({ record, label }) => ({ candidate: candidate({ concernKind: "system_mechanism", mechanism: "rejection_filter", source: "rejection_rule_evidence", id: `rejection:${record.timestamp || "unknown"}:${hashText(record.type + record.text)}`, facts: { type: record.type, neutralLabel: label, timestamp: record.timestamp || undefined, origin: record.origin }, evidence: { rawReasonCodes: record.reasons, textPreview: truncate(cleanText(record.text, raw), 120), textAvailable: true }, provenance: classifyProvenance({ rejection: record }, context), versionContext: versionContextFor(record, currentPackageVersion), heuristicFlags: [], reviewQuestions: ["Are rejection filters over-filtering durable decisions or under-filtering non-durable candidates for this workspace?"], nextCommands: ["memory-diag rejected --verbose"], }), timestamp: record.timestamp, tieId: record.timestamp || "unknown", textHash: hashText(record.text), })); return candidates; } function neutralRejectionLabel(record: NormalizedRejection): "architecture_like_rejected_candidate" | "ambiguous_rejected_candidate" | "status_or_hard_reason_evidence" { const hardReasons = record.reasons.filter(reason => HARD_OR_NOISE_REASON_CODES.has(reason)); const statusLike = /\b(?:implemented|added|updated|fixed|completed|reviewed|tests?|CI|commit|wave|phase|task|session)\b/i.test(record.text); const architectureLike = /\b(?:architecture|retention|migration|schema|policy|model|dedup|identity|parser|formatter|diagnostic|evidence|cap|window|api|contract)\b/i.test(record.text); if (architectureLike && hardReasons.length === 0 && !statusLike) return "architecture_like_rejected_candidate"; if (hardReasons.length > 0 || statusLike) return "status_or_hard_reason_evidence"; return "ambiguous_rejected_candidate"; } function buildReabsorptionCandidates(matches: ReabsorbedMatch[], context: ProvenanceContextInputs, raw: boolean, currentPackageVersion: string): DatedCandidateInput[] { const candidates = matches.map(match => ({ candidate: candidate({ concernKind: "system_mechanism", mechanism: "rejection_filter", source: "reabsorption_evidence", id: `reabsorbed:${match.activeMemory.id}:${hashText(match.key)}`, facts: { activeMemoryId: match.activeMemory.id, type: match.activeMemory.type, rejectedAt: match.record.timestamp || undefined }, evidence: { rawReasonCodes: match.record.reasons, textPreview: truncate(cleanText(match.record.text, raw), 120), textAvailable: true }, provenance: classifyProvenance({ rejection: match.record, reabsorbed: true }, context), versionContext: versionContextFor(match.record, currentPackageVersion), heuristicFlags: [], reviewQuestions: ["Did later context make this rejected candidate worth reviewing for filter calibration?"], nextCommands: ["memory-diag rejected --verbose", `memory-diag explain ${match.activeMemory.id}`], }), timestamp: match.record.timestamp, tieId: match.activeMemory.id, textHash: hashText(match.key), })); return candidates; } function buildReinforcementCandidates(events: EvidenceEventV1[], context: ProvenanceContextInputs, raw: boolean, currentPackageVersion: string): DatedCandidateInput[] { const blocked = events.filter(event => isReinforcementEvent(event) && event.reasonCodes.includes("reinforcement_window_blocked")); const grouped = [...groupBy(blocked, event => event.memory?.memoryId ?? "unknown").entries()].map(([memoryId, group]) => ({ memoryId, group })); const repeated = grouped.filter(item => item.group.length > 1).map(item => { const latest = newestEvent(item.group); return { candidate: candidate({ concernKind: "system_mechanism", mechanism: "reinforcement_rule", source: "numbered_command_evidence", id: `reinforcement:${item.memoryId}:${item.group.length}`, facts: { memoryId: item.memoryId, blockCount: item.group.length, refs: uniqueStrings(item.group.map(event => String(event.details?.ref ?? "")).filter(Boolean)).sort() }, evidence: { eventIds: item.group.map(event => event.eventId), rawReasonCodes: uniqueStrings(item.group.flatMap(event => event.reasonCodes)).sort(), textAvailable: false }, provenance: classifyProvenance({ event: latest }, context), versionContext: versionContextFor(latest, currentPackageVersion), heuristicFlags: [flag("repeated_reinforcement_window_block", "Repeated reinforcement window block inventory", `${item.group.length} reinforcement attempts were blocked for memory ${item.memoryId}`)], reviewQuestions: ["What reinforcement block patterns are present for repeated reinforce intent?"], nextCommands: ["memory-diag commands --verbose", `memory-diag explain ${item.memoryId}`], }), timestamp: latest?.createdAt, tieId: item.memoryId, textHash: item.memoryId, }; }); const malformed = events.filter(isMalformedCommandEvent).map(event => ({ candidate: candidate({ concernKind: "system_mechanism", mechanism: "reinforcement_rule", source: "numbered_command_evidence", id: `malformed-command:${event.eventId}`, facts: { eventType: event.type, createdAt: event.createdAt }, evidence: { eventIds: [event.eventId], rawReasonCodes: event.reasonCodes, textPreview: event.textPreview ? truncate(cleanText(event.textPreview, raw), 120) : undefined, textAvailable: Boolean(event.textPreview) }, provenance: classifyProvenance({ event }, context), versionContext: versionContextFor(event, currentPackageVersion), heuristicFlags: [flag("malformed_numbered_command", "Malformed numbered-memory command evidence", "command parser rejected a memory command form")], reviewQuestions: ["Do numbered-memory command rules match how agents actually express reinforcement intent?"], nextCommands: ["memory-diag commands --verbose"], }), timestamp: event.createdAt, tieId: event.eventId, textHash: event.eventId, })); return [...repeated, ...malformed]; } function buildEvictionCandidates( disappearances: ReturnType, events: EvidenceEventV1[], context: ProvenanceContextInputs, raw: boolean, generatedAt: string, currentPackageVersion: string, ): DatedCandidateInput[] { const recentCapacity = events.filter(event => event.type === "memory_removed_capacity" && isWithinDaysOf(event.createdAt, generatedAt, RECENT_EVICTION_DAYS)); const capacityCandidates = recentCapacity.map(event => ({ candidate: candidate({ concernKind: "system_mechanism", mechanism: "eviction_cap", source: "eviction_cap_evidence", id: `eviction:${event.eventId}`, facts: { ...(safeDetails(event.details, raw) ?? {}), createdAt: event.createdAt, memoryId: event.memory?.memoryId, type: event.memory?.type }, evidence: { eventIds: [event.eventId], rawReasonCodes: event.reasonCodes, textPreview: event.textPreview ? truncate(cleanText(event.textPreview, raw), 120) : undefined, textAvailable: Boolean(event.textPreview) }, provenance: classifyProvenance({ event }, context), versionContext: versionContextFor(event, currentPackageVersion), heuristicFlags: [flag("recent_capacity_removal", "Recent capacity-removal inventory", "memory_removed_capacity appeared within the recent eviction window")], reviewQuestions: ["What capacity-removal inventory is present for this memory?"], nextCommands: ["memory-diag missing --verbose --explain"], }), timestamp: event.createdAt, tieId: event.eventId, textHash: event.textPreview ? hashText(event.textPreview) : event.eventId, })); const unknownCandidates = disappearances .filter(row => row.classification === "historical_absent_unknown_reason") .map(row => { const latest = newestEvent(row.events); return { candidate: candidate({ concernKind: "system_mechanism", mechanism: "eviction_cap", source: "missing_evidence", id: `missing:${row.id}`, facts: { memoryId: row.id, terminalType: row.terminalType, eventCount: row.events.length }, evidence: { eventIds: row.events.map(event => event.eventId), rawReasonCodes: uniqueStrings(row.events.flatMap(event => event.reasonCodes)).sort(), textPreview: latest?.textPreview ? truncate(cleanText(latest.textPreview, raw), 120) : undefined, textAvailable: Boolean(latest?.textPreview) }, provenance: classifyProvenance({ event: latest }, context), versionContext: versionContextFor(latest, currentPackageVersion), heuristicFlags: [flag("unknown_disappearance", "Unversioned disappearance inventory", `memory ${row.id} has evidence but is not active`)], reviewQuestions: ["What unversioned disappearance inventory exists for this memory?"], nextCommands: ["memory-diag missing --verbose --explain"], }), timestamp: latest?.createdAt, tieId: row.id, textHash: row.id, }; }); return [...capacityCandidates, ...unknownCandidates]; } function safeDetails(details: EvidenceEventV1["details"], raw: boolean): Record | undefined { if (!details) return undefined; return Object.fromEntries(Object.entries(details).map(([key, value]) => [key, typeof value === "string" ? cleanText(value, raw) : value])); } function buildIdentityCandidates(model: MemoryInspectionReadModel, activeMemories: LongTermMemoryEntry[], context: ProvenanceContextInputs, raw: boolean, currentPackageVersion: string): DatedCandidateInput[] { const replacementCandidates = model.evidenceEvents .filter(event => event.type === "memory_replaced_numbered_ref" || event.type === "promotion_superseded") .map(event => ({ candidate: candidate({ concernKind: "system_mechanism", mechanism: "identity_dedup", source: "identity_dedup_evidence", id: `identity-event:${event.eventId}`, facts: { eventType: event.type, memoryId: event.memory?.memoryId, relationRoles: event.relations?.map(relation => relation.role) ?? [] }, evidence: { eventIds: [event.eventId], rawReasonCodes: event.reasonCodes, textPreview: event.textPreview ? truncate(cleanText(event.textPreview, raw), 120) : undefined, textAvailable: Boolean(event.textPreview) }, provenance: classifyProvenance({ event }, context), versionContext: versionContextFor(event, currentPackageVersion), heuristicFlags: [], reviewQuestions: ["Are identity and dedup rules preserving separate memories when expected to remain distinct?"], nextCommands: ["memory-diag commands --verbose", event.memory?.memoryId ? `memory-diag explain ${event.memory.memoryId}` : "memory-diag missing --verbose --explain"], }), timestamp: event.createdAt, tieId: event.eventId, textHash: event.eventId, })); const duplicateCandidates = duplicateGroups(activeMemories, model.evidenceEvents).map(group => ({ candidate: candidate({ concernKind: "system_mechanism", mechanism: "identity_dedup", source: "identity_dedup_evidence", id: `duplicate:${group.id}`, facts: { memoryIds: group.memoryIds, basis: group.basis }, evidence: { eventIds: group.memoryIds.flatMap(id => (model.evidenceByMemoryId.get(id) ?? []).map(event => event.eventId)), rawReasonCodes: [], textAvailable: false }, provenance: classifyProvenance({}, context), heuristicFlags: [], reviewQuestions: ["Are exact duplicate text or identity groups expected for this workspace?"], nextCommands: group.memoryIds.map(id => `memory-diag explain ${id}`).slice(0, 3), }), timestamp: undefined, tieId: group.id, textHash: group.id, })); return [...replacementCandidates, ...duplicateCandidates]; } function candidate(input: ReviewBoardCandidate): ReviewBoardCandidate { return input; } function selectRepresentative(items: DatedCandidateInput[], verbose: boolean): DatedCandidateInput[] { const sorted = [...items].sort((a, b) => { const timeDelta = timestampValue(b.timestamp) - timestampValue(a.timestamp); if (timeDelta !== 0) return timeDelta; const idDelta = a.tieId.localeCompare(b.tieId); if (idDelta !== 0) return idDelta; return (a.textHash ?? "").localeCompare(b.textHash ?? ""); }); return verbose ? sorted : sorted.slice(0, REPRESENTATIVE_CANDIDATE_LIMIT); } function timestampValue(iso: string | undefined): number { const time = iso ? new Date(iso).getTime() : 0; return Number.isFinite(time) ? time : 0; } function newestEvent(events: EvidenceEventV1[]): EvidenceEventV1 | undefined { return [...events].sort((a, b) => timestampValue(b.createdAt) - timestampValue(a.createdAt) || a.eventId.localeCompare(b.eventId))[0]; } function flag(id: string, label: string, evidence: string): HeuristicFlag { return { id, label, evidence, caveat: "This flag is a prompt for review, not a conclusion.", }; } function systemMechanismQuestions(): string[] { return [ "Are rejection rules over-filtering durable decisions or under-filtering non-durable candidates for this workspace?", "What block patterns are present for repeated reinforcement intent?", "What cap occupancy and capacity-removal inventory is present?", "Are identity and dedup rules collapsing items expected to remain separate, or not collapsing equivalent items?", ]; } function memoryContentQuestions(): string[] { return [ "Does this memory remain durable and actionable for future sessions?", "Is this memory non-stale, specific, and supported by available evidence?", "Does this memory overlap with other active memories in a way a reviewer should consider?", ]; } function nextCommands(): string[] { return [ "memory-diag rejected --verbose", "memory-diag missing --verbose --explain", "memory-diag commands --verbose", "memory-diag explain ", ]; } function countProvenanceClassifications(candidates: ReviewBoardCandidate[]): Record { const counts = Object.fromEntries(PROVENANCE_CLASSIFICATIONS.map(classification => [classification, 0])) as Record; for (const provenanceItem of candidates.map(candidate => candidate.provenance)) { if (!provenanceItem) continue; counts[provenanceItem.classification] += 1; } return counts; } function hashText(text: string): string { return createHash("sha256").update(text).digest("hex").slice(0, 12); }