Extract memory organ legacy mirror repair
This commit is contained in:
parent
6ac3645039
commit
02122dbfa3
6 changed files with 225 additions and 202 deletions
|
|
@ -31,7 +31,7 @@ Git history and smoke artifacts carry routine proof. `state/evidence.jsonl` shou
|
|||
- `scripts/run-void-moderator-rumination.ps1` now records reviewed Discord cursors and speech delivery receipts through typed self-state operations. The old `set-cursor` and `record-delivery-receipt` commands were removed from `scripts/moderation-state-store.mjs`.
|
||||
- The unattended moderation child no longer gets to commit whole-state JSON edits. It reads `.voidbot/private/moderation-agent-state.json` as a compatibility projection, writes proposed operation payloads to `.voidbot/status/moderation-rumination-operations.json`, and the parent runner applies them through `scripts/void-self-state.mjs apply-operation`.
|
||||
- `scripts/simulate-void-mood.mjs` now sends sleep-cycle and speaking-pressure results through typed operations after its legacy save, so scheduled-runtime typed documents stay current. The memory organ itself still mutates the legacy monolith and remains the next major surgery.
|
||||
- `scripts/void-memory-organ.mjs` has started being split by concern. Shared limits/text sets, generic utilities, vector/embedder code, and memory-record intake now live in `scripts/void-memory-organ-constants.mjs`, `scripts/void-memory-organ-utils.mjs`, `scripts/void-memory-organ-vector.mjs`, and `scripts/void-memory-organ-records.mjs`, with `scripts/void-memory-organ-shared.mjs` as a temporary re-export shim.
|
||||
- `scripts/void-memory-organ.mjs` has started being split by concern. Shared limits/text sets, generic utilities, vector/embedder code, memory-record intake, and legacy mirror repair now live in `scripts/void-memory-organ-constants.mjs`, `scripts/void-memory-organ-utils.mjs`, `scripts/void-memory-organ-vector.mjs`, `scripts/void-memory-organ-records.mjs`, and `scripts/void-memory-organ-legacy-mirrors.mjs`, with `scripts/void-memory-organ-shared.mjs` as a temporary re-export shim.
|
||||
- Most of the deterministic cleanup in `scripts/void-memory-organ.mjs` is recent compensator cruft from failed attempts to manually clean an exploded state file. Do not preserve it as an earned organ. Architect the state boundary so that cleanup pile is unnecessary.
|
||||
- Sleep/distillation must be rewritten with a meaning-preservation contract: concrete subject, claim/question, evidence refs, live tension, and future-action implication survive compression. Bulk can die. Meaning does not get put through the office shredder and called a dream.
|
||||
- The owner Codex lane is no longer one swollen file; orchestration, runtime/parsing, rendering, and shared helpers are split under `packages/providers/src/owner-codex-*.ts`.
|
||||
|
|
|
|||
169
scripts/void-memory-organ-legacy-mirrors.mjs
Normal file
169
scripts/void-memory-organ-legacy-mirrors.mjs
Normal file
|
|
@ -0,0 +1,169 @@
|
|||
import {
|
||||
MAX_EDGE_COUNT,
|
||||
MAX_POST_NAP_NOVELTY_CHECKS,
|
||||
MAX_REPO_SWEEP_MEMORIES,
|
||||
MAX_RUNTIME_ARCHIVE_EXCURSIONS,
|
||||
MAX_RUNTIME_REPO_ACTIVITY_MEMORIES,
|
||||
} from "./void-memory-organ-constants.mjs";
|
||||
import {
|
||||
cloneJson,
|
||||
compactNarrative,
|
||||
compareMemoryFreshness,
|
||||
ensureArray,
|
||||
ensureObject,
|
||||
isObject,
|
||||
looksLegacyThoughtSurface,
|
||||
readString,
|
||||
} from "./void-memory-organ-utils.mjs";
|
||||
|
||||
export function trimLegacyRuntimeResidue({ runtime }) {
|
||||
const memoryResonance = ensureObject(runtime.memory_resonance);
|
||||
const recentEdges = ensureArray(memoryResonance.recent_edges).filter(isObject);
|
||||
memoryResonance.recent_edges = recentEdges
|
||||
.filter((edge) => !looksLegacyThoughtSurface(readString(edge, "leftLabel") ?? "", readString(edge, "summary") ?? ""))
|
||||
.slice(-MAX_EDGE_COUNT);
|
||||
runtime.memory_resonance = memoryResonance;
|
||||
}
|
||||
|
||||
export function reconcileLegacyStateMirrors({ state, memories, runtime, now }) {
|
||||
const timestamp = now.toISOString();
|
||||
const bridge = ensureObject(runtime.bridge);
|
||||
const runtimeThoughtLanes = ensureObject(runtime.thought_lanes);
|
||||
const activeThoughts = ensureArray(ensureObject(runtime.incubation).active_thoughts).filter(isObject);
|
||||
const activeTensions = ensureArray(runtime.active_tensions).filter(isObject);
|
||||
|
||||
state.archive_excursions = [];
|
||||
state.repo_sweeps = ensureArray(memories.repo_sweeps)
|
||||
.filter(isObject)
|
||||
.map((entry) => compactRepoSweepMirror(entry))
|
||||
.slice(0, MAX_REPO_SWEEP_MEMORIES);
|
||||
state.repo_activity_memories = ensureArray(runtime.repo_activity_memories)
|
||||
.filter(isObject)
|
||||
.map((entry) => compactRepoSweepMirror(entry))
|
||||
.slice(0, MAX_RUNTIME_REPO_ACTIVITY_MEMORIES);
|
||||
state.recent_archive_excursions = ensureArray(runtime.recent_archive_excursions)
|
||||
.filter(isObject)
|
||||
.map((entry) => compactArchiveExcursionMirror(entry))
|
||||
.slice(0, MAX_RUNTIME_ARCHIVE_EXCURSIONS);
|
||||
state.recent_novelty_checks = ensureArray(runtime.recent_novelty_checks)
|
||||
.filter(isObject)
|
||||
.map((entry) => compactNoveltyCheckMirror(entry))
|
||||
.slice(0, MAX_POST_NAP_NOVELTY_CHECKS);
|
||||
state.recent_activity = compactRecentActivityMirror(runtime.recent_activity, runtime, timestamp);
|
||||
state.source_coverage = cloneJson(ensureObject(runtime.source_coverage));
|
||||
state.speaking_bias = cloneJson(ensureObject(runtime.speaking_bias));
|
||||
state.lastReviewSummary = readString(runtime, "lastReviewSummary") ?? readString(runtime, "last_review_summary") ?? null;
|
||||
state.lastReviewSummaryShort =
|
||||
readString(runtime, "lastReviewSummaryShort") ?? readString(runtime, "last_review_summary_short") ?? null;
|
||||
state.last_review_summary = state.lastReviewSummary;
|
||||
state.last_review_summary_short = state.lastReviewSummaryShort;
|
||||
state.thought_lanes = {
|
||||
analytic: buildThoughtLaneMirror({
|
||||
sourceValue: runtimeThoughtLanes.analytic,
|
||||
fallback: activeTensions[0],
|
||||
timestamp,
|
||||
}),
|
||||
associative: buildThoughtLaneMirror({
|
||||
sourceValue: runtimeThoughtLanes.associative,
|
||||
fallback: activeThoughts[0] ?? bridge,
|
||||
timestamp,
|
||||
}),
|
||||
};
|
||||
}
|
||||
|
||||
function compactRepoSweepMirror(entry) {
|
||||
return {
|
||||
timestamp: readString(entry, "timestamp") ?? null,
|
||||
memoryId: readString(entry, "memoryId") ?? null,
|
||||
summary: compactNarrative(readString(entry, "summary") ?? ""),
|
||||
repoNames: ensureStringArray(entry.repoNames).slice(0, 3),
|
||||
whyItMattered: compactNarrative(readString(entry, "whyItMattered") ?? ""),
|
||||
};
|
||||
}
|
||||
|
||||
function compactArchiveExcursionMirror(entry) {
|
||||
return {
|
||||
memoryId: readString(entry, "memoryId") ?? null,
|
||||
timestamp: readString(entry, "timestamp") ?? null,
|
||||
topicHint: compactNarrative(readString(entry, "topicHint") ?? ""),
|
||||
whyItWasFresh: compactNarrative(readString(entry, "whyItWasFresh") ?? ""),
|
||||
};
|
||||
}
|
||||
|
||||
function compactNoveltyCheckMirror(entry) {
|
||||
return {
|
||||
timestamp: readString(entry, "timestamp") ?? null,
|
||||
query: compactNarrative(readString(entry, "query") ?? ""),
|
||||
candidate: compactNarrative(readString(entry, "candidate") ?? ""),
|
||||
result: readString(entry, "result") ?? null,
|
||||
note: compactNarrative(readString(entry, "note") ?? ""),
|
||||
};
|
||||
}
|
||||
|
||||
function compactRecentActivityMirror(value, runtime, timestamp) {
|
||||
const recentActivity = isObject(value) ? cloneJson(value) : {};
|
||||
recentActivity.checkedAt = readString(recentActivity, "checkedAt") ?? timestamp;
|
||||
if (typeof recentActivity.summary === "string") {
|
||||
recentActivity.summary = compactNarrative(recentActivity.summary);
|
||||
}
|
||||
if (typeof recentActivity.lastDecisionRationale === "string") {
|
||||
recentActivity.lastDecisionRationale = compactNarrative(recentActivity.lastDecisionRationale);
|
||||
}
|
||||
if (!recentActivity.summary) {
|
||||
recentActivity.summary =
|
||||
compactNarrative(readString(runtime, "lastReviewSummary") ?? readString(runtime, "last_review_summary") ?? "") ??
|
||||
"";
|
||||
}
|
||||
return recentActivity;
|
||||
}
|
||||
|
||||
function buildThoughtLaneMirror({ sourceValue, fallback, timestamp }) {
|
||||
const summary = summarizeThoughtLaneSource(sourceValue, fallback);
|
||||
return summary
|
||||
? [
|
||||
{
|
||||
timestamp,
|
||||
summary,
|
||||
},
|
||||
]
|
||||
: [];
|
||||
}
|
||||
|
||||
function summarizeThoughtLaneSource(sourceValue, fallback) {
|
||||
if (typeof sourceValue === "string") {
|
||||
return compactNarrative(sourceValue);
|
||||
}
|
||||
|
||||
if (isObject(sourceValue)) {
|
||||
const threads = ensureArray(sourceValue.active_threads).filter(isObject);
|
||||
if (threads.length > 0) {
|
||||
const latest = threads
|
||||
.slice()
|
||||
.sort(compareMemoryFreshness)[0];
|
||||
return compactNarrative(
|
||||
[readString(latest, "topic"), readString(latest, "claim"), readString(latest, "counterweight")]
|
||||
.filter(Boolean)
|
||||
.join(". "),
|
||||
);
|
||||
}
|
||||
const description = readString(sourceValue, "description");
|
||||
if (description) {
|
||||
return compactNarrative(description);
|
||||
}
|
||||
}
|
||||
|
||||
if (isObject(fallback)) {
|
||||
return compactNarrative(
|
||||
[
|
||||
readString(fallback, "topic"),
|
||||
readString(fallback, "summary"),
|
||||
readString(fallback, "claim"),
|
||||
readString(fallback, "opinion"),
|
||||
]
|
||||
.filter(Boolean)
|
||||
.join(". "),
|
||||
);
|
||||
}
|
||||
|
||||
return null;
|
||||
}
|
||||
|
|
@ -145,6 +145,17 @@ export function cloneJson(value) {
|
|||
return JSON.parse(JSON.stringify(value));
|
||||
}
|
||||
|
||||
export function looksLegacyThoughtSurface(label, summary) {
|
||||
return (
|
||||
/\//.test(label) ||
|
||||
/^Recurring seam across /i.test(summary) ||
|
||||
/^Dream-compressed a seam around /i.test(summary) ||
|
||||
/^Self-facing seam around /i.test(summary) ||
|
||||
/^Archive-facing seam around /i.test(summary) ||
|
||||
/What part of this thought wants embodiment/i.test(summary)
|
||||
);
|
||||
}
|
||||
|
||||
export function parseIsoTimestamp(value) {
|
||||
if (typeof value !== "string" || value.length === 0) {
|
||||
return null;
|
||||
|
|
@ -253,3 +264,39 @@ export function stripBom(input) {
|
|||
export function isObject(value) {
|
||||
return typeof value === "object" && value !== null && !Array.isArray(value);
|
||||
}
|
||||
|
||||
export function compactNarrative(value) {
|
||||
const normalized = normalizeText(value);
|
||||
if (!normalized) {
|
||||
return normalized;
|
||||
}
|
||||
const firstSentence = normalized.match(/^.*?[.!?](?:\s|$)/)?.[0]?.trim();
|
||||
const candidate = firstSentence && firstSentence.length >= 24 ? firstSentence : normalized;
|
||||
return candidate.length > 220 ? `${candidate.slice(0, 217).trimEnd()}...` : candidate;
|
||||
}
|
||||
|
||||
export function compactLabel(value) {
|
||||
const normalized = normalizeText(value);
|
||||
if (!normalized) {
|
||||
return normalized;
|
||||
}
|
||||
if (/incremental sweep found/i.test(normalized)) {
|
||||
const repoMatch = normalized.match(/`([^`]+)`/);
|
||||
const repoName = repoMatch?.[1] ?? "repo";
|
||||
return `${repoName} repo sweep`;
|
||||
}
|
||||
if (/no new discord traffic|one new discord message|no messages arrived/i.test(normalized)) {
|
||||
return "room pass";
|
||||
}
|
||||
return normalized.length > 96 ? `${normalized.slice(0, 93).trimEnd()}...` : normalized;
|
||||
}
|
||||
|
||||
export function capDistinctStrings(entries, limit) {
|
||||
return [...new Set(ensureStringArray(entries))].slice(0, limit);
|
||||
}
|
||||
|
||||
export function compareMemoryFreshness(left, right) {
|
||||
const leftTime = parseIsoTimestamp(readString(left, "lastObservedAt") ?? readString(left, "timestamp") ?? "") ?? 0;
|
||||
const rightTime = parseIsoTimestamp(readString(right, "lastObservedAt") ?? readString(right, "timestamp") ?? "") ?? 0;
|
||||
return rightTime - leftTime;
|
||||
}
|
||||
|
|
|
|||
|
|
@ -62,6 +62,11 @@ import {
|
|||
mergeStringArrays,
|
||||
newestIsoTimestamp,
|
||||
cloneJson,
|
||||
looksLegacyThoughtSurface,
|
||||
compactNarrative,
|
||||
compactLabel,
|
||||
capDistinctStrings,
|
||||
compareMemoryFreshness,
|
||||
parseIsoTimestamp,
|
||||
hashString,
|
||||
parseDotEnvSafe,
|
||||
|
|
@ -81,6 +86,7 @@ import {
|
|||
isObject,
|
||||
} from "./void-memory-organ-shared.mjs";
|
||||
import { collectMemoryRecords, extractYear } from "./void-memory-organ-records.mjs";
|
||||
import { reconcileLegacyStateMirrors, trimLegacyRuntimeResidue } from "./void-memory-organ-legacy-mirrors.mjs";
|
||||
|
||||
const repoRoot = resolve(dirname(fileURLToPath(import.meta.url)), "..");
|
||||
|
||||
|
|
@ -419,17 +425,6 @@ function translateLegacyThoughtLikeEntry({
|
|||
};
|
||||
}
|
||||
|
||||
function looksLegacyThoughtSurface(label, summary) {
|
||||
return (
|
||||
/\//.test(label) ||
|
||||
/^Recurring seam across /i.test(summary) ||
|
||||
/^Dream-compressed a seam around /i.test(summary) ||
|
||||
/^Self-facing seam around /i.test(summary) ||
|
||||
/^Archive-facing seam around /i.test(summary) ||
|
||||
/What part of this thought wants embodiment/i.test(summary)
|
||||
);
|
||||
}
|
||||
|
||||
function choosePreferredThoughtLabel(entry) {
|
||||
return (
|
||||
sanitizePreferredThoughtLabel(readString(entry, "subjectLabel") ?? "") ??
|
||||
|
|
@ -1065,194 +1060,6 @@ function distillStateSurface({ memories, runtime }) {
|
|||
}
|
||||
}
|
||||
|
||||
function compactNarrative(value) {
|
||||
const normalized = normalizeText(value);
|
||||
if (!normalized) {
|
||||
return normalized;
|
||||
}
|
||||
const firstSentence = normalized.match(/^.*?[.!?](?:\s|$)/)?.[0]?.trim();
|
||||
const candidate = firstSentence && firstSentence.length >= 24 ? firstSentence : normalized;
|
||||
return candidate.length > 220 ? `${candidate.slice(0, 217).trimEnd()}...` : candidate;
|
||||
}
|
||||
|
||||
function compactLabel(value) {
|
||||
const normalized = normalizeText(value);
|
||||
if (!normalized) {
|
||||
return normalized;
|
||||
}
|
||||
if (/incremental sweep found/i.test(normalized)) {
|
||||
const repoMatch = normalized.match(/`([^`]+)`/);
|
||||
const repoName = repoMatch?.[1] ?? "repo";
|
||||
return `${repoName} repo sweep`;
|
||||
}
|
||||
if (/no new discord traffic|one new discord message|no messages arrived/i.test(normalized)) {
|
||||
return "room pass";
|
||||
}
|
||||
return normalized.length > 96 ? `${normalized.slice(0, 93).trimEnd()}...` : normalized;
|
||||
}
|
||||
|
||||
function capDistinctStrings(entries, limit) {
|
||||
return [...new Set(ensureStringArray(entries))].slice(0, limit);
|
||||
}
|
||||
|
||||
function compareMemoryFreshness(left, right) {
|
||||
const leftTime = parseIsoTimestamp(readString(left, "lastObservedAt") ?? readString(left, "timestamp") ?? "") ?? 0;
|
||||
const rightTime = parseIsoTimestamp(readString(right, "lastObservedAt") ?? readString(right, "timestamp") ?? "") ?? 0;
|
||||
return rightTime - leftTime;
|
||||
}
|
||||
|
||||
function trimLegacyRuntimeResidue({ runtime }) {
|
||||
const memoryResonance = ensureObject(runtime.memory_resonance);
|
||||
const recentEdges = ensureArray(memoryResonance.recent_edges).filter(isObject);
|
||||
memoryResonance.recent_edges = recentEdges
|
||||
.filter((edge) => !looksLegacyThoughtSurface(readString(edge, "leftLabel") ?? "", readString(edge, "summary") ?? ""))
|
||||
.slice(-MAX_EDGE_COUNT);
|
||||
runtime.memory_resonance = memoryResonance;
|
||||
}
|
||||
|
||||
function reconcileLegacyStateMirrors({ state, memories, runtime, now }) {
|
||||
const timestamp = now.toISOString();
|
||||
const bridge = ensureObject(runtime.bridge);
|
||||
const runtimeThoughtLanes = ensureObject(runtime.thought_lanes);
|
||||
const activeThoughts = ensureArray(ensureObject(runtime.incubation).active_thoughts).filter(isObject);
|
||||
const activeTensions = ensureArray(runtime.active_tensions).filter(isObject);
|
||||
|
||||
state.archive_excursions = [];
|
||||
state.repo_sweeps = ensureArray(memories.repo_sweeps)
|
||||
.filter(isObject)
|
||||
.map((entry) => compactRepoSweepMirror(entry))
|
||||
.slice(0, MAX_REPO_SWEEP_MEMORIES);
|
||||
state.repo_activity_memories = ensureArray(runtime.repo_activity_memories)
|
||||
.filter(isObject)
|
||||
.map((entry) => compactRepoSweepMirror(entry))
|
||||
.slice(0, MAX_RUNTIME_REPO_ACTIVITY_MEMORIES);
|
||||
state.recent_archive_excursions = ensureArray(runtime.recent_archive_excursions)
|
||||
.filter(isObject)
|
||||
.map((entry) => compactArchiveExcursionMirror(entry))
|
||||
.slice(0, MAX_RUNTIME_ARCHIVE_EXCURSIONS);
|
||||
state.recent_novelty_checks = ensureArray(runtime.recent_novelty_checks)
|
||||
.filter(isObject)
|
||||
.map((entry) => compactNoveltyCheckMirror(entry))
|
||||
.slice(0, MAX_POST_NAP_NOVELTY_CHECKS);
|
||||
state.recent_activity = compactRecentActivityMirror(runtime.recent_activity, runtime, timestamp);
|
||||
state.source_coverage = cloneJson(ensureObject(runtime.source_coverage));
|
||||
state.speaking_bias = cloneJson(ensureObject(runtime.speaking_bias));
|
||||
state.lastReviewSummary = readString(runtime, "lastReviewSummary") ?? readString(runtime, "last_review_summary") ?? null;
|
||||
state.lastReviewSummaryShort =
|
||||
readString(runtime, "lastReviewSummaryShort") ?? readString(runtime, "last_review_summary_short") ?? null;
|
||||
state.last_review_summary = state.lastReviewSummary;
|
||||
state.last_review_summary_short = state.lastReviewSummaryShort;
|
||||
state.thought_lanes = {
|
||||
analytic: buildThoughtLaneMirror({
|
||||
sourceValue: runtimeThoughtLanes.analytic,
|
||||
fallback: activeTensions[0],
|
||||
timestamp,
|
||||
}),
|
||||
associative: buildThoughtLaneMirror({
|
||||
sourceValue: runtimeThoughtLanes.associative,
|
||||
fallback: activeThoughts[0] ?? bridge,
|
||||
timestamp,
|
||||
}),
|
||||
};
|
||||
}
|
||||
|
||||
function compactRepoSweepMirror(entry) {
|
||||
return {
|
||||
timestamp: readString(entry, "timestamp") ?? null,
|
||||
memoryId: readString(entry, "memoryId") ?? null,
|
||||
summary: compactNarrative(readString(entry, "summary") ?? ""),
|
||||
repoNames: ensureStringArray(entry.repoNames).slice(0, 3),
|
||||
whyItMattered: compactNarrative(readString(entry, "whyItMattered") ?? ""),
|
||||
};
|
||||
}
|
||||
|
||||
function compactArchiveExcursionMirror(entry) {
|
||||
return {
|
||||
memoryId: readString(entry, "memoryId") ?? null,
|
||||
timestamp: readString(entry, "timestamp") ?? null,
|
||||
topicHint: compactNarrative(readString(entry, "topicHint") ?? ""),
|
||||
whyItWasFresh: compactNarrative(readString(entry, "whyItWasFresh") ?? ""),
|
||||
};
|
||||
}
|
||||
|
||||
function compactNoveltyCheckMirror(entry) {
|
||||
return {
|
||||
timestamp: readString(entry, "timestamp") ?? null,
|
||||
query: compactNarrative(readString(entry, "query") ?? ""),
|
||||
candidate: compactNarrative(readString(entry, "candidate") ?? ""),
|
||||
result: readString(entry, "result") ?? null,
|
||||
note: compactNarrative(readString(entry, "note") ?? ""),
|
||||
};
|
||||
}
|
||||
|
||||
function compactRecentActivityMirror(value, runtime, timestamp) {
|
||||
const recentActivity = isObject(value) ? cloneJson(value) : {};
|
||||
recentActivity.checkedAt = readString(recentActivity, "checkedAt") ?? timestamp;
|
||||
if (typeof recentActivity.summary === "string") {
|
||||
recentActivity.summary = compactNarrative(recentActivity.summary);
|
||||
}
|
||||
if (typeof recentActivity.lastDecisionRationale === "string") {
|
||||
recentActivity.lastDecisionRationale = compactNarrative(recentActivity.lastDecisionRationale);
|
||||
}
|
||||
if (!recentActivity.summary) {
|
||||
recentActivity.summary =
|
||||
compactNarrative(readString(runtime, "lastReviewSummary") ?? readString(runtime, "last_review_summary") ?? "") ??
|
||||
"";
|
||||
}
|
||||
return recentActivity;
|
||||
}
|
||||
|
||||
function buildThoughtLaneMirror({ sourceValue, fallback, timestamp }) {
|
||||
const summary = summarizeThoughtLaneSource(sourceValue, fallback);
|
||||
return summary
|
||||
? [
|
||||
{
|
||||
timestamp,
|
||||
summary,
|
||||
},
|
||||
]
|
||||
: [];
|
||||
}
|
||||
|
||||
function summarizeThoughtLaneSource(sourceValue, fallback) {
|
||||
if (typeof sourceValue === "string") {
|
||||
return compactNarrative(sourceValue);
|
||||
}
|
||||
|
||||
if (isObject(sourceValue)) {
|
||||
const threads = ensureArray(sourceValue.active_threads).filter(isObject);
|
||||
if (threads.length > 0) {
|
||||
const latest = threads
|
||||
.slice()
|
||||
.sort(compareMemoryFreshness)[0];
|
||||
return compactNarrative(
|
||||
[readString(latest, "topic"), readString(latest, "claim"), readString(latest, "counterweight")]
|
||||
.filter(Boolean)
|
||||
.join(". "),
|
||||
);
|
||||
}
|
||||
const description = readString(sourceValue, "description");
|
||||
if (description) {
|
||||
return compactNarrative(description);
|
||||
}
|
||||
}
|
||||
|
||||
if (isObject(fallback)) {
|
||||
return compactNarrative(
|
||||
[
|
||||
readString(fallback, "topic"),
|
||||
readString(fallback, "summary"),
|
||||
readString(fallback, "claim"),
|
||||
readString(fallback, "opinion"),
|
||||
]
|
||||
.filter(Boolean)
|
||||
.join(". "),
|
||||
);
|
||||
}
|
||||
|
||||
return null;
|
||||
}
|
||||
|
||||
async function ensureSemanticVector({ record, embedder, now }) {
|
||||
const prior = isObject(record.entry.semanticVector) ? record.entry.semanticVector : {};
|
||||
const contentHash = hashString(record.text);
|
||||
|
|
@ -3586,4 +3393,3 @@ function mapCountEntries(map, keyName) {
|
|||
.map(([key, count]) => ({ [keyName]: key, count }))
|
||||
.sort((left, right) => right.count - left.count);
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -97,3 +97,4 @@
|
|||
{"ts":"2026-05-16T11:17:09Z","type":"implementation","status":"ok","note":"The unattended moderation runner no longer commits child edits from moderation-agent-state.json. The child is instructed to treat the JSON as read-only and write a JSON array of typed operation payloads to .voidbot/status/moderation-rumination-operations.json; the parent applies each operation through scripts/void-self-state.mjs before recording cursor/receipt operations. Cheap parser/typecheck verification passed; full scheduled dry run was not run to avoid mutating live moderation state during code verification."}
|
||||
{"ts":"2026-05-16T11:20:42Z","type":"implementation","status":"ok","note":"Mood drift now sends sleep-cycle and speaking-pressure results through typed self-state operations after saving the legacy monolith, so scheduled-runtime typed documents stay current. The larger memory organ still mutates legacy state directly; next surgery is to move memory/incubation/candidate outputs behind operations and then remove working-projection materialization."}
|
||||
{"ts":"2026-05-16T11:44:36Z","type":"implementation","status":"ok","note":"Started breaking up scripts/void-memory-organ.mjs by ownership. Extracted shared constants/text sets, generic utilities, vector/embedder substrate, and memory-record intake into separate modules while keeping the public reconcileSemanticMemoryState entrypoint stable. Hash/template smoke exercised the extracted modules without touching live state. Remaining monolith work is policy organs: cleanup/translation, graph/cluster thought synthesis, incubation/bridge, sleep/identity/speech, and agency."}
|
||||
{"ts":"2026-05-16T11:48:20Z","type":"implementation","status":"ok","note":"Continued void-memory-organ split by extracting legacy mirror repair into scripts/void-memory-organ-legacy-mirrors.mjs and moving compacting helpers into shared utilities. This isolates compatibility sludge from actual memory/thought policy and cuts the main file to about 3.4k lines. Template smoke and syntax/type checks passed."}
|
||||
|
|
|
|||
|
|
@ -149,4 +149,4 @@ rejected_paths:
|
|||
|
||||
current_status:
|
||||
summary: VoidBot's ordinary bot/worker/provider/RAG/Postgres/Qdrant structure is still conceptually sound, and the moderation/mood/private-self-state rebuild has cut the worst mutation path: routine runner writes now cross a typed operation service instead of direct JSON projection commits. Remaining risk is that the legacy monolith and read-only projection still exist for compatibility, mood drift still refreshes that projection, and memory/sleep cleanup remains swollen compensator code.
|
||||
next_action: JSON projection authority is no longer a routine moderation-runner write path, and mood drift now updates typed scheduled-runtime documents for sleep/speaking pressure. The memory organ split has begun: constants, utilities, vector/embedder substrate, and record intake are separate files. Next split the remaining policy organs by ownership, starting with cleanup/translation versus graph/incubation/agency, then move memory-maintenance/incubation/candidate outputs behind typed operations and delete `legacyJsonPath`/working-projection materialization.
|
||||
next_action: JSON projection authority is no longer a routine moderation-runner write path, and mood drift now updates typed scheduled-runtime documents for sleep/speaking pressure. The memory organ split has begun: constants, utilities, vector/embedder substrate, record intake, and legacy mirror repair are separate files. Next split the remaining policy organs by ownership, starting with cleanup/translation versus graph/incubation/agency, then move memory-maintenance/incubation/candidate outputs behind typed operations and delete `legacyJsonPath`/working-projection materialization.
|
||||
|
|
|
|||
Loading…
Reference in a new issue