Files
opencode-working-memory/scripts/memory-diag/formatters/quality.ts
T
2026-05-13 21:18:24 +08:00

468 lines
28 KiB
TypeScript

import { cleanText, formatDetails } from "../text.ts";
import type {
CandidateProvenance,
HeuristicFlag,
ProvenanceClassification,
RejectionVersionFacts,
ReinforcementVersionFacts,
ReviewBoardActiveMemory,
ReviewBoardCandidate,
ReviewBoardReport,
EvictionVersionFacts,
VersionAvailability,
VersionCoverage,
VersionedMechanismFacts,
} from "../quality-review-model.ts";
const PROVENANCE_ORDER: ProvenanceClassification[] = [
"explicit_migration_evidence",
"legacy_unversioned_format",
"reabsorbed_post_rejection",
"suspected_pre_migration_legacy",
"likely_current_behavior",
"unversioned_ambiguous",
];
const REVIEW_FLAG_CAVEAT = "This flag is a prompt for review, not a conclusion.";
export function buildQualityJSON(report: ReviewBoardReport, raw = false): unknown {
if (raw) return report;
return redactUnknown(report);
}
export function formatQualityReviewBoard(
report: ReviewBoardReport,
options: { verbose?: boolean },
): string {
const bullet = "-";
const lines: string[] = [];
lines.push("Memory quality review board");
lines.push("Purpose: evidence for human/agent review only; no automatic judgment or cleanup.");
lines.push("Producer version note: historical records do not include package/plugin version; provenance below is inferred.");
lines.push("Primary review purpose: SYSTEM MECHANISM observations (filters, reinforcement, eviction/caps, identity/dedup).");
lines.push("Secondary review purpose: MEMORY CONTENT quality (staleness, durability, redundancy, specificity).");
lines.push("");
pushEvidenceProvenance(lines, report, bullet);
lines.push("");
pushSystemMechanismFacts(lines, report, bullet);
lines.push("");
pushMemoryContentFacts(lines, report, bullet);
lines.push("");
pushSystemMechanismCandidates(lines, report, bullet);
lines.push("");
pushMemoryContentCandidates(lines, report, bullet, options);
lines.push("");
pushReviewQuestions(lines, report, bullet);
lines.push("");
pushNextCommands(lines, report, bullet);
return lines.join("\n");
}
function pushEvidenceProvenance(lines: string[], report: ReviewBoardReport, bullet: string): void {
const context = report.provenanceContext;
const instrumentation = report.facts.systemMechanisms.instrumentation;
lines.push("Evidence provenance");
lines.push(` ${bullet} method: migration/timestamp/format inference`);
lines.push(` ${bullet} confidence: ${context.confidenceDisclaimer}`);
lines.push(` ${bullet} Producer coverage: ${instrumentation.evidenceEventsWithProducer} of ${instrumentation.evidenceEventsTotal} evidence events instrumented`);
lines.push(` ${bullet} Rejection producer coverage: ${instrumentation.rejectionRecordsWithProducer} of ${instrumentation.rejectionRecordsTotal} rejection records instrumented`);
lines.push(` ${bullet} instrumentation versions: ${formatCounts(instrumentation.instrumentationVersions)}`);
lines.push(` ${bullet} migration timeline: ${formatMigrationTimeline(context.migrationTimeline)}`);
if (context.lastActivityAt) lines.push(` ${bullet} last activity: ${context.lastActivityAt}`);
}
function pushSystemMechanismFacts(lines: string[], report: ReviewBoardReport, bullet: string): void {
const facts = report.facts.systemMechanisms;
lines.push("Facts - system mechanisms");
lines.push(" Provenance counts for mechanism evidence");
lines.push(` ${bullet} ${formatProvenanceCounts(report.provenanceContext.countsByClassification)}`);
lines.push(" Rejection filters");
pushAnswerability(lines, report.answerability?.rejectionFilters, " ");
lines.push(` ${bullet} rejected records: ${facts.rejectionFilters.totalRecords} (unique: ${facts.rejectionFilters.uniqueTexts})`);
lines.push(` ${bullet} raw reason-code distribution: ${formatCounts(facts.rejectionFilters.byRawReasonCode)}`);
lines.push(` ${bullet} type distribution: ${formatCounts(facts.rejectionFilters.byType)}`);
lines.push(` ${bullet} ambiguous/architecture-like rejected candidates: ${facts.rejectionFilters.ambiguousOrArchitectureLike}`);
lines.push(` ${bullet} status-or-hard-reason evidence: ${facts.rejectionFilters.hardReasonOrNoiseHeuristic}`);
lines.push(` ${bullet} re-absorbed rejected texts: ${facts.rejectionFilters.reabsorbedRejectedTexts}`);
if (facts.versionedFacts) pushVersionAnalysis(lines, facts.versionedFacts.rejectionFilters, facts.versionedFacts.versionCoverage, formatRejectionVersionFacts, bullet);
lines.push(" Reinforcement rules");
pushAnswerability(lines, report.answerability?.reinforcementRules, " ");
lines.push(` ${bullet} reinforce attempts: ${facts.reinforcementRules.reinforceEvents}, reinforced: ${facts.reinforcementRules.reinforcedEvents}, rejected/blocked: ${facts.reinforcementRules.rejectedOrBlockedEvents}`);
lines.push(` ${bullet} reinforcement-window blocked: ${facts.reinforcementRules.windowBlockedEvents} (rate: ${formatPercent(facts.reinforcementRules.windowBlockRate)})`);
lines.push(` ${bullet} Exact block reasons: ${formatCounts(facts.reinforcementRules.blocksByExactReason)}`);
lines.push(` ${bullet} window blocks by UTC day: ${formatCounts(facts.reinforcementRules.windowBlocksByUtcDay)}`);
lines.push(` ${bullet} block details missing: ${facts.reinforcementRules.blockDetailsMissing}`);
lines.push(` ${bullet} repeated blocks by memory: ${formatRepeatedBlocks(facts.reinforcementRules.repeatedBlocksByMemory)}`);
lines.push(` ${bullet} malformed command events: ${facts.reinforcementRules.malformedCommandEvents}`);
if (facts.versionedFacts) pushVersionAnalysis(lines, facts.versionedFacts.reinforcementRules, facts.versionedFacts.versionCoverage, formatReinforcementVersionFacts, bullet);
lines.push(" Eviction and caps");
pushAnswerability(lines, report.answerability?.evictionAndCaps, " ");
lines.push(` ${bullet} active memories: ${facts.evictionAndCaps.activeMemories} / ${facts.evictionAndCaps.maxEntries}`);
lines.push(` ${bullet} rendered memories: ${facts.evictionAndCaps.renderedMemories}`);
lines.push(` ${bullet} cap occupancy: ${formatFullCaps(facts.evictionAndCaps.fullCaps, facts.evictionAndCaps.typeCounts, facts.evictionAndCaps.typeCaps, facts.evictionAndCaps.activeMemories, facts.evictionAndCaps.maxEntries)}`);
lines.push(` ${bullet} capacity removals: total=${facts.evictionAndCaps.removedByCapacity}, global=${facts.evictionAndCaps.removedByGlobalCap}, type=${facts.evictionAndCaps.removedByTypeCap}`);
lines.push(` ${bullet} Removals with snapshot: ${facts.evictionAndCaps.recentCapacityRemovalsWithSnapshot}`);
lines.push(` ${bullet} Removals without snapshot: ${facts.evictionAndCaps.capacitySnapshotsMissing} (historical)`);
if (facts.evictionAndCaps.highestRankRemoved) lines.push(` ${bullet} highest-rank removed snapshot: ${formatHighestRankRemoved(facts.evictionAndCaps.highestRankRemoved)}`);
lines.push(` ${bullet} recent evictions by type: ${formatCounts(facts.evictionAndCaps.recentEvictionsByType)}`);
lines.push(` ${bullet} recent evicted content shown: ${facts.evictionAndCaps.recentEvictedContentShown}`);
if (facts.versionedFacts) pushVersionAnalysis(lines, facts.versionedFacts.evictionAndCaps, facts.versionedFacts.versionCoverage, formatEvictionVersionFacts, bullet);
lines.push(" Unknown disappearances");
pushAnswerability(lines, report.answerability?.unknownDisappearances, " ");
lines.push(` ${bullet} unversioned disappearance inventory: evidence-only=${facts.evictionAndCaps.missingEvidenceOnly}, unknown=${facts.evictionAndCaps.unknownDisappearances}`);
lines.push(" Identity and dedup");
pushAnswerability(lines, report.answerability?.identityAndDedup, " ");
lines.push(` ${bullet} replacements: total=${facts.identityAndDedup.replacementEvents}, same-type=${facts.identityAndDedup.sameTypeReplacementEvents}, cross-type=${facts.identityAndDedup.crossTypeReplacementEvents}`);
lines.push(` ${bullet} superseded entries: ${facts.identityAndDedup.supersededEntries}`);
lines.push(` ${bullet} exact duplicate/identity groups identified: ${facts.identityAndDedup.duplicateTextOrIdentityGroups}`);
}
function pushMemoryContentFacts(lines: string[], report: ReviewBoardReport, bullet: string): void {
const facts = report.facts.memoryContent;
lines.push("Facts - memory content");
pushAnswerability(lines, report.answerability?.memoryContent, " ");
lines.push(` ${bullet} rendered memories: ${facts.renderedMemories}`);
lines.push(` ${bullet} evidence coverage: ${facts.evidenceCoverage.covered} / ${facts.evidenceCoverage.total}`);
lines.push(` ${bullet} type counts: ${formatTypeCountsWithCaps(facts.typeCounts, facts.typeCaps)}`);
lines.push(` ${bullet} weakest/strongest active memory previews: weakest=${formatMemoryPreviews(facts.weakestActiveMemories)}; strongest=${formatMemoryPreviews(facts.strongestActiveMemories)}`);
}
function pushAnswerability(
lines: string[],
assessment: NonNullable<ReviewBoardReport["answerability"]>[keyof NonNullable<ReviewBoardReport["answerability"]>] | undefined,
indent: string,
): void {
if (!assessment) return;
const suffix = assessment.level === "partial" ? " — causal fields exist, but human content judgment is still required" : "";
lines.push(`${indent}(Answerability: ${assessment.level}${suffix})`);
lines.push(`${indent}Output permission: ${assessment.outputPermission}`);
}
function pushVersionAnalysis<TFacts>(
lines: string[],
mechanism: VersionedMechanismFacts<TFacts>,
coverage: VersionCoverage,
formatFacts: (facts: TFacts) => string,
bullet: string,
): void {
lines.push(" Version analysis by producer version");
lines.push(` Version-stamp coverage (all evidence/rejection records, not mechanism problem counts): Coverage: ${formatCoveragePercent(coverage.coveragePercent)} of ${formatInteger(coverage.totalEvents)} records carry a version stamp (${formatInteger(coverage.currentVersionEvents)} current, ${formatInteger(coverage.previousVersionEvents)} previous, ${formatInteger(coverage.unknownVersionEvents)} unknown/unversioned). Comparison will become meaningful as new events accumulate.`);
if (coverage.isTransitional) {
lines.push(" NOTE: Version coverage is below 50%. Current-version comparisons may not be representative.");
}
lines.push(` ${mechanismOpportunityDescription(mechanism)}`);
for (const group of ["current", "previous", "unknown_unversioned"] as const) {
const bucket = mechanism.buckets[group];
lines.push(` ${bullet} ${bucket.label}: opportunities=${bucket.opportunityCount}, observed=${bucket.observedPatternCount}, sample=${bucket.sampleAssessment}, answerability=${bucket.answerabilityLevel}`);
if (Object.keys(bucket.producerVersions).length > 0) lines.push(` producer versions: ${formatCounts(bucket.producerVersions)}`);
lines.push(` composition: ${formatVersionAvailability(bucket.versionAvailability)}`);
lines.push(` facts: ${formatFacts(bucket.facts)}`);
}
lines.push(` ${bullet} inference: ${mechanism.inference.message}`);
lines.push(` diagnostic strength: ${diagnosticStrengthLabel(mechanism)}`);
const diagnosticLine = currentMechanismDiagnosticLine(mechanism);
if (diagnosticLine) lines.push(` ${diagnosticLine}`);
if (mechanism.diagnosticQuestions) {
for (const question of mechanism.diagnosticQuestions) {
lines.push(` diagnostic question: ${question.question} Evidence: ${question.evidence.join(", ")}`);
}
}
lines.push(` caveat: ${mechanism.inference.caveat}`);
}
function diagnosticStrengthLabel<TFacts>(mechanism: VersionedMechanismFacts<TFacts>): string {
const current = mechanism.buckets.current;
const strength = mechanism.inference.status === "no_current_version_opportunities" || current.opportunityCount === 0
? "unavailable"
: current.opportunityCount < mechanism.sampleThreshold
? "weak"
: "moderate";
return hasCurrentCausalDetail(mechanism) ? `${strength}; causal detail available` : strength;
}
function hasCurrentCausalDetail<TFacts>(mechanism: VersionedMechanismFacts<TFacts>): boolean {
const currentFacts = mechanism.buckets.current.facts;
return isReinforcementVersionFacts(currentFacts) && Object.keys(currentFacts.blocksByExactReason).length > 0;
}
function currentMechanismDiagnosticLine<TFacts>(mechanism: VersionedMechanismFacts<TFacts>): string | undefined {
const current = mechanism.buckets.current;
if (!isReinforcementVersionFacts(current.facts)) return undefined;
const facts = current.facts;
const parts: string[] = [];
if (Object.keys(facts.blocksByExactReason).length > 0) parts.push(`current block reasons=${formatCounts(facts.blocksByExactReason)}`);
if (facts.blockDetailsMissing > 0) parts.push(`current block details missing=${facts.blockDetailsMissing}`);
if (parts.length === 0) return undefined;
parts.push(`sample=${current.opportunityCount} attempts`);
return `diagnostic: ${parts.join("; ")}`;
}
function isReinforcementVersionFacts(facts: unknown): facts is ReinforcementVersionFacts {
return typeof facts === "object"
&& facts !== null
&& "blocksByExactReason" in facts
&& "blockDetailsMissing" in facts
&& "windowBlockedEvents" in facts;
}
function mechanismOpportunityDescription<TFacts>(mechanism: VersionedMechanismFacts<TFacts>): string {
if (mechanism.opportunityName === "rejection candidates") return "Mechanism opportunities below are reviewable rejection candidates only; render/accounting events are excluded.";
if (mechanism.opportunityName === "attempts") return "Mechanism opportunities below are reinforcement attempts only; render/accounting events are excluded.";
if (mechanism.opportunityName === "capacity removals") return "Mechanism opportunities below are capacity removals only; render/accounting events are excluded.";
return `Mechanism opportunities below are ${mechanism.opportunityName} only; render/accounting events are excluded.`;
}
function formatVersionAvailability(availability: VersionAvailability): string {
const parts = [
availability.noProducerFields > 0 ? `no producer fields=${availability.noProducerFields}` : undefined,
availability.unknownProducerVersion > 0 ? `unknown version=${availability.unknownProducerVersion}` : undefined,
availability.emptyProducerVersion > 0 ? `empty version=${availability.emptyProducerVersion}` : undefined,
availability.knownProducerVersion > 0 ? `known version=${availability.knownProducerVersion}` : undefined,
].filter((part): part is string => Boolean(part));
return parts.length === 0 ? "(empty bucket)" : parts.join(", ");
}
function formatRejectionVersionFacts(facts: RejectionVersionFacts): string {
return `records=${facts.totalRecords}, candidates=${facts.candidateRecords}, raw reason codes=${formatCounts(facts.byRawReasonCode)}, types=${formatCounts(facts.byType)}`;
}
function formatReinforcementVersionFacts(facts: ReinforcementVersionFacts): string {
return `reinforce attempts=${facts.reinforceEvents}, window blocked=${facts.windowBlockedEvents}, exact reasons=${formatCounts(facts.blocksByExactReason)}, block details missing=${facts.blockDetailsMissing}`;
}
function formatEvictionVersionFacts(facts: EvictionVersionFacts): string {
return `capacity removals=${facts.removedByCapacity}, with snapshot=${facts.recentCapacityRemovalsWithSnapshot}, missing snapshot=${facts.capacitySnapshotsMissing}`;
}
function pushSystemMechanismCandidates(lines: string[], report: ReviewBoardReport, bullet: string): void {
const display = report.provenanceContext.candidateDisplay;
if (report.provenanceContext.candidateLimit && display && display.shown < display.total) {
lines.push(`System mechanism review candidates (representative; ${display.shown} shown of ${display.total} total; limit ${report.provenanceContext.candidateLimit} per mechanism category)`);
} else {
lines.push("System mechanism review candidates");
}
pushCandidateGroup(lines, "Rejection filter evidence", candidatesFor(report, ["rejection_rule_evidence"]), bullet);
pushCandidateGroup(lines, "Re-absorption evidence", candidatesFor(report, ["reabsorption_evidence"]), bullet);
pushCandidateGroup(lines, "Reinforcement rule evidence", candidatesFor(report, ["numbered_command_evidence"]), bullet);
pushCandidateGroup(lines, "Eviction/cap evidence", candidatesFor(report, ["eviction_cap_evidence", "missing_evidence"]), bullet);
pushCandidateGroup(lines, "Identity/dedup evidence", candidatesFor(report, ["identity_dedup_evidence"]), bullet);
}
function pushCandidateGroup(lines: string[], title: string, candidates: ReviewBoardCandidate[], bullet: string): void {
lines.push(` ${title}`);
if (candidates.length === 0) {
lines.push(" (none)");
return;
}
const shared = sharedProvenance(candidates);
if (shared) lines.push(` shared provenance for displayed candidates in this group: ${formatProvenance(shared)}`);
for (const candidate of candidates) pushCandidate(lines, candidate, bullet, shared);
}
function pushCandidate(lines: string[], candidate: ReviewBoardCandidate, bullet: string, groupProvenance?: CandidateProvenance): void {
const rawReasonCodes = candidate.evidence.rawReasonCodes && candidate.evidence.rawReasonCodes.length > 0
? candidate.evidence.rawReasonCodes.join(", ")
: "none";
const question = candidate.reviewQuestions[0] ?? "What should a reviewer infer from this evidence?";
lines.push(` ${bullet} concern=${formatConcern(candidate.concernKind)} id=${candidate.id} source=${candidate.source} mechanism=${candidate.mechanism ?? "unspecified"} raw reason codes=${rawReasonCodes} question=${question}`);
if (candidate.provenance && (!groupProvenance || formatProvenance(candidate.provenance) !== formatProvenance(groupProvenance))) {
lines.push(` provenance: ${formatProvenance(candidate.provenance)}`);
}
if (candidate.evidence.eventIds && candidate.evidence.eventIds.length > 0) lines.push(` event ids: ${candidate.evidence.eventIds.join(", ")}`);
if (candidate.evidence.textAvailable) {
lines.push(` text preview: ${candidate.evidence.textPreview ?? "available but empty after redaction"}`);
} else {
lines.push(" text preview: unavailable in historical evidence");
}
lines.push(` facts: ${formatCandidateFacts(candidate.facts)}`);
pushHeuristicFlags(lines, candidate.heuristicFlags, " ", bullet);
}
function pushMemoryContentCandidates(lines: string[], report: ReviewBoardReport, bullet: string, options: { verbose?: boolean }): void {
const display = report.activeMemoryDisplay;
lines.push("Memory content review candidates");
if (report.reviewQuestions.memoryContent.length > 0) {
lines.push(" Standard review questions (applicable to all active memories below):");
for (const question of report.reviewQuestions.memoryContent) lines.push(` ${bullet} ${question}`);
}
if (display.total === 0) {
lines.push(" Active memories (none)");
return;
}
if (display.total <= display.threshold) {
lines.push(` Active memories (showing all ${display.total} because <= ${display.threshold})`);
} else if (display.mode === "all" || options.verbose) {
lines.push(` Active memories (showing all ${display.total} because --verbose)`);
} else {
lines.push(` Active memories (showing ${display.shown} of ${display.total})`);
lines.push(` Showing ${display.shown} of ${display.total} active memories. Use --verbose or --json for all active memory text.`);
}
display.items.forEach((item, index) => pushActiveMemory(lines, item, index + 1, bullet, report.reviewQuestions.memoryContent));
}
function pushActiveMemory(lines: string[], item: ReviewBoardActiveMemory, index: number, bullet: string, standardQuestions: string[]): void {
const strength = typeof item.strength === "number" ? item.strength.toFixed(3) : "unknown";
lines.push(` [${index}] id=${item.id} type=${item.type} source=${item.source} status=${item.status} strength=${strength}`);
lines.push(" text: " + indentContinuation(item.text, " "));
const rawReasonCodes = item.evidence.rawReasonCodes.length > 0 ? item.evidence.rawReasonCodes.join(", ") : "none";
lines.push(` evidence: events=${item.evidence.eventCount} raw reason codes=${rawReasonCodes}`);
if (item.provenance) lines.push(` provenance: ${formatProvenance(item.provenance)}`);
pushHeuristicFlags(lines, item.heuristicFlags, " ", bullet);
if (questionsEqual(item.reviewQuestions, standardQuestions)) return;
const additionalQuestions = item.reviewQuestions.filter(question => !standardQuestions.includes(question));
if (additionalQuestions.length > 0 && additionalQuestions.length < item.reviewQuestions.length) {
lines.push(" additional review questions:");
for (const question of additionalQuestions) lines.push(` ${bullet} ${question}`);
return;
}
lines.push(" review questions:");
for (const question of item.reviewQuestions) lines.push(` ${bullet} ${question}`);
}
function pushHeuristicFlags(lines: string[], flags: HeuristicFlag[], indent: string, bullet: string): void {
if (flags.length === 0) return;
lines.push(`${indent}heuristic flags:`);
for (const flag of flags) {
const caveat = flag.caveat || REVIEW_FLAG_CAVEAT;
lines.push(`${indent} ${bullet} ${flag.label}: ${flag.evidence}. ${caveat}`);
}
}
function pushReviewQuestions(lines: string[], report: ReviewBoardReport, bullet: string): void {
lines.push("Review questions");
lines.push(" SYSTEM MECHANISM");
for (const question of report.reviewQuestions.systemMechanism) lines.push(` ${bullet} ${question}`);
lines.push(" MEMORY CONTENT");
for (const question of report.reviewQuestions.memoryContent) lines.push(` ${bullet} ${question}`);
}
function pushNextCommands(lines: string[], report: ReviewBoardReport, bullet: string): void {
lines.push("Next commands");
for (const command of report.nextCommands) lines.push(` ${bullet} ${command}`);
}
function candidatesFor(report: ReviewBoardReport, sources: ReviewBoardCandidate["source"][]): ReviewBoardCandidate[] {
const sourceSet = new Set(sources);
return report.reviewCandidates.filter(candidate => candidate.concernKind === "system_mechanism" && sourceSet.has(candidate.source));
}
function sharedProvenance(candidates: ReviewBoardCandidate[]): CandidateProvenance | undefined {
if (candidates.length <= 1) return undefined;
const first = candidates[0]?.provenance;
if (!first) return undefined;
const key = formatProvenance(first);
return candidates.every(candidate => candidate.provenance && formatProvenance(candidate.provenance) === key) ? first : undefined;
}
function formatConcern(concern: ReviewBoardCandidate["concernKind"]): string {
return concern === "system_mechanism" ? "SYSTEM MECHANISM" : "MEMORY CONTENT";
}
function formatMigrationTimeline(timeline: ReviewBoardReport["provenanceContext"]["migrationTimeline"]): string {
if (timeline.length === 0) return "(none)";
return timeline.map(row => `${row.migrationId}=${row.presentInStore ? "present" : "absent"}${row.firstEvidenceAt ? ` firstEvidenceAt=${row.firstEvidenceAt}` : ""}`).join(", ");
}
function formatProvenanceCounts(counts: Record<ProvenanceClassification, number>): string {
return PROVENANCE_ORDER.map(classification => `${classification}=${counts[classification] ?? 0}`).join(", ");
}
function formatCounts(counts: Record<string, number>): string {
const entries = Object.entries(counts).sort((a, b) => b[1] - a[1] || a[0].localeCompare(b[0]));
return entries.length === 0 ? "(none)" : entries.map(([key, count]) => `${key}=${count}`).join(", ");
}
function formatTypeCountsWithCaps(counts: Record<string, number>, caps: Record<string, number>): string {
const keys = uniqueSorted([...Object.keys(caps), ...Object.keys(counts)]);
return keys.length === 0 ? "(none)" : keys.map(key => `${key} ${counts[key] ?? 0}/${caps[key] ?? "?"}`).join(", ");
}
function formatFullCaps(fullCaps: string[], typeCounts: Record<string, number>, typeCaps: Record<string, number>, active: number, maxEntries: number): string {
if (fullCaps.length === 0) return "(none)";
return fullCaps.map(cap => cap === "global" ? `global ${active}/${maxEntries}` : `${cap} ${typeCounts[cap] ?? 0}/${typeCaps[cap] ?? "?"}`).join(", ");
}
function formatRepeatedBlocks(blocks: ReviewBoardReport["facts"]["systemMechanisms"]["reinforcementRules"]["repeatedBlocksByMemory"]): string {
if (blocks.length === 0) return "(none)";
return blocks.map(block => `${block.memoryId} count=${block.count} refs=${block.refs.join("|") || "none"} raw reason codes=${block.rawReasonCodes.join("|") || "none"}`).join(", ");
}
function formatHighestRankRemoved(snapshot: NonNullable<ReviewBoardReport["facts"]["systemMechanisms"]["evictionAndCaps"]["highestRankRemoved"]>): string {
const parts = [
`eventId=${snapshot.eventId}`,
snapshot.memoryId ? `memoryId=${snapshot.memoryId}` : undefined,
snapshot.type ? `type=${snapshot.type}` : undefined,
`rankAtRemoval=${snapshot.rankAtRemoval}`,
typeof snapshot.strengthAtRemoval === "number" ? `strengthAtRemoval=${snapshot.strengthAtRemoval}` : undefined,
].filter((part): part is string => Boolean(part));
return parts.join(" ");
}
function formatMemoryPreviews(items: ReviewBoardReport["facts"]["memoryContent"]["weakestActiveMemories"]): string {
if (items.length === 0) return "(none)";
return items.map(item => `${item.id} type=${item.type} strength=${typeof item.strength === "number" ? item.strength.toFixed(3) : "unknown"} text=${JSON.stringify(item.textPreview)}`).join(" | ");
}
function formatPercent(value: number): string {
return `${(Number.isFinite(value) ? value * 100 : 0).toFixed(1)}%`;
}
function formatCoveragePercent(value: number): string {
if (!Number.isFinite(value)) return "0%";
return Number.isInteger(value) ? `${value}%` : `${value.toFixed(1)}%`;
}
function formatInteger(value: number): string {
return new Intl.NumberFormat("en-US", { maximumFractionDigits: 0 }).format(value);
}
function formatProvenance(provenance: CandidateProvenance): string {
return `${provenance.classification} confidence=${provenance.confidence}; basis=${provenance.basis.join("; ") || "unavailable"}; caveat=${provenance.interpretationCaveat}`;
}
function formatCandidateFacts(facts: Record<string, unknown>): string {
if (Object.keys(facts).length === 0) return "(none)";
return formatDetails(Object.fromEntries(
Object.entries(facts).map(([key, value]) => [key, formatFactValue(value)]),
));
}
function formatFactValue(value: unknown): string | number | boolean | string[] | undefined {
if (value === undefined) return undefined;
if (typeof value === "string" || typeof value === "number" || typeof value === "boolean") return value;
if (value === null) return "null";
if (Array.isArray(value)) return value.map(item => typeof item === "string" || typeof item === "number" || typeof item === "boolean" || item === null ? String(item) : stringifyUnknown(item));
return stringifyUnknown(value);
}
function stringifyUnknown(value: unknown): string {
try {
const serialized = JSON.stringify(value);
return serialized === undefined ? String(value) : serialized;
} catch {
return String(value);
}
}
function questionsEqual(a: string[], b: string[]): boolean {
return a.length === b.length && a.every((value, index) => value === b[index]);
}
function indentContinuation(text: string, indent: string): string {
return text.split("\n").map((line, index) => index === 0 ? line : `${indent}${line}`).join("\n");
}
function uniqueSorted(values: string[]): string[] {
return [...new Set(values)].sort();
}
function redactUnknown(value: unknown): unknown {
if (typeof value === "string") return cleanText(value, false);
if (Array.isArray(value)) return value.map(item => redactUnknown(item));
if (!value || typeof value !== "object") return value;
return Object.fromEntries(Object.entries(value).map(([key, item]) => [key, redactUnknown(item)]));
}