Expose repo content hints to rumination
This commit is contained in:
parent
ffbe3eb77d
commit
8ce19260c2
4 changed files with 118 additions and 1 deletions
|
|
@ -28,6 +28,7 @@ Perform one scheduled Void rumination pass in this workspace. Think from the sup
|
|||
- Do not orbit the rumination runner, fixture plumbing, or this prompt just because that is the freshest repo motion.
|
||||
- Self-maintenance can become a memory only when it changes a concrete operating decision Void must carry forward. Otherwise, use it as local run context and write `[]`.
|
||||
- Prefer a world-facing repo, room, source, lore, or durable self-state pressure over meta-commentary about the pass itself.
|
||||
- `repoActivity` may include `contentHints` from changed text files. Treat those hints as a trailhead, not evidence enough for a final belief. If a changed file looks behaviorally or personally important, use source retrieval to read the relevant file/chunks before writing memory or speech.
|
||||
4. Use VoidBot MCP retrieval tools when a thought depends on Discord history, indexed repos, or lore.
|
||||
5. A new memory from rumination is short-term. Use `record_short_term_memory`, not durable memory operations. It should sound like something Void actually noticed, not a form label. Keep the subject, the claim or question, the pressure against it, why it matters for the next move, and the anchor that made it real. Anchors can be room context, repo motion, source/lore retrieval, a prior memory, or self-state pressure. If no anchor is available, mark that honestly with `anchor:missing`.
|
||||
- Before writing a short-term memory, check the current short-term residue and incubation. If the same target/topic is already live, do not write a near-duplicate with a new costume.
|
||||
|
|
|
|||
|
|
@ -392,16 +392,120 @@ function enrichCommit(repoPath, commit) {
|
|||
...commit,
|
||||
diffstat: parseDiffstat(summaryLine),
|
||||
changedPaths,
|
||||
contentHints: buildCommitContentHints(repoPath, commit.hash, changedPaths),
|
||||
};
|
||||
} catch {
|
||||
return {
|
||||
...commit,
|
||||
diffstat: null,
|
||||
changedPaths: [],
|
||||
contentHints: [],
|
||||
};
|
||||
}
|
||||
}
|
||||
|
||||
function buildCommitContentHints(repoPath, commitHash, changedPaths) {
|
||||
return changedPaths
|
||||
.filter((path) => isHintableTextPath(path))
|
||||
.slice(0, 4)
|
||||
.map((path) => buildFileContentHint(repoPath, commitHash, path))
|
||||
.filter(Boolean);
|
||||
}
|
||||
|
||||
function isHintableTextPath(path) {
|
||||
return /\.(md|mdx|txt|json|jsonc|ya?ml|toml|ts|tsx|js|jsx|mjs|cjs|rs|cs|ps1|py|html|css|scss)$/i.test(path);
|
||||
}
|
||||
|
||||
function buildFileContentHint(repoPath, commitHash, path) {
|
||||
const currentText = readCommittedFile(repoPath, commitHash, path);
|
||||
const addedLines = readAddedLines(repoPath, commitHash, path);
|
||||
const frontmatter = parseFrontmatter(currentText);
|
||||
const headings = readMarkdownHeadings(currentText).slice(0, 4);
|
||||
const excerpts = addedLines
|
||||
.filter((line) => line.trim().length > 0 && !line.trim().startsWith("---"))
|
||||
.slice(0, 6);
|
||||
|
||||
if (!frontmatter && headings.length === 0 && excerpts.length === 0) {
|
||||
return undefined;
|
||||
}
|
||||
|
||||
return {
|
||||
path,
|
||||
frontmatter,
|
||||
headings,
|
||||
addedExcerpts: excerpts,
|
||||
};
|
||||
}
|
||||
|
||||
function readCommittedFile(repoPath, commitHash, path) {
|
||||
try {
|
||||
return execGit(repoPath, ["show", `${commitHash}:${path}`]);
|
||||
} catch {
|
||||
return "";
|
||||
}
|
||||
}
|
||||
|
||||
function readAddedLines(repoPath, commitHash, path) {
|
||||
try {
|
||||
return execGit(repoPath, ["show", commitHash, "--format=", "--unified=0", "--", path])
|
||||
.split(/\r?\n/)
|
||||
.filter((line) => line.startsWith("+") && !line.startsWith("+++"))
|
||||
.map((line) => line.slice(1).trim())
|
||||
.filter(Boolean)
|
||||
.slice(0, 12);
|
||||
} catch {
|
||||
return [];
|
||||
}
|
||||
}
|
||||
|
||||
function parseFrontmatter(text) {
|
||||
const match = text.match(/^---\r?\n([\s\S]*?)\r?\n---/);
|
||||
if (!match) {
|
||||
return undefined;
|
||||
}
|
||||
|
||||
const result = {};
|
||||
let activeListKey;
|
||||
for (const rawLine of match[1].split(/\r?\n/)) {
|
||||
const line = rawLine.trimEnd();
|
||||
const listItem = line.match(/^\s*-\s+(.*)$/);
|
||||
if (listItem && activeListKey) {
|
||||
result[activeListKey].push(stripWrappingQuotes(listItem[1].trim()));
|
||||
continue;
|
||||
}
|
||||
|
||||
activeListKey = undefined;
|
||||
const pair = line.match(/^([A-Za-z0-9_-]+):\s*(.*)$/);
|
||||
if (!pair) {
|
||||
continue;
|
||||
}
|
||||
|
||||
const [, key, rawValue] = pair;
|
||||
if (rawValue.trim().length === 0) {
|
||||
result[key] = [];
|
||||
activeListKey = key;
|
||||
continue;
|
||||
}
|
||||
|
||||
if (["title", "description", "author", "date", "socialDeck"].includes(key)) {
|
||||
result[key] = stripWrappingQuotes(rawValue.trim());
|
||||
}
|
||||
}
|
||||
|
||||
return Object.keys(result).length > 0 ? result : undefined;
|
||||
}
|
||||
|
||||
function readMarkdownHeadings(text) {
|
||||
return text
|
||||
.split(/\r?\n/)
|
||||
.map((line) => line.match(/^(#{1,3})\s+(.+)$/)?.[2]?.trim())
|
||||
.filter(Boolean);
|
||||
}
|
||||
|
||||
function stripWrappingQuotes(value) {
|
||||
return value.replace(/^"(.*)"$/, "$1").replace(/^'(.*)'$/, "$1");
|
||||
}
|
||||
|
||||
function parseDiffstat(summaryLine) {
|
||||
if (!summaryLine) {
|
||||
return null;
|
||||
|
|
|
|||
|
|
@ -151,7 +151,11 @@ function Project-OpenCasesForRumination {
|
|||
param($Cases, [DateTime] $Now)
|
||||
|
||||
return @(
|
||||
@(Convert-ToValueArray -Value $Cases) | ForEach-Object {
|
||||
@(Convert-ToValueArray -Value $Cases) |
|
||||
Where-Object {
|
||||
-not (Test-RuminationCaseTerminal -Status (Get-ObjectPropertyString -Value $_ -Name "status"))
|
||||
} |
|
||||
ForEach-Object {
|
||||
@{
|
||||
sourceMessageId = Get-ObjectPropertyString -Value $_ -Name "sourceMessageId"
|
||||
status = Get-ObjectPropertyString -Value $_ -Name "status"
|
||||
|
|
@ -169,6 +173,12 @@ function Project-OpenCasesForRumination {
|
|||
)
|
||||
}
|
||||
|
||||
function Test-RuminationCaseTerminal {
|
||||
param([string] $Status)
|
||||
|
||||
return @("answered", "resolved", "closed", "retired", "dropped").Contains($Status)
|
||||
}
|
||||
|
||||
function Project-SpeechReceiptsForRumination {
|
||||
param($Receipts, [DateTime] $Now)
|
||||
|
||||
|
|
@ -351,6 +361,7 @@ function Project-RepoCommitForRumination {
|
|||
subject = Get-ObjectPropertyString -Value $Commit -Name "subject"
|
||||
diffstat = Get-ObjectPropertyValue -Value $Commit -Name "diffstat"
|
||||
changedPaths = @(Convert-ToValueArray -Value (Get-ObjectPropertyValue -Value $Commit -Name "changedPaths"))
|
||||
contentHints = @(Convert-ToValueArray -Value (Get-ObjectPropertyValue -Value $Commit -Name "contentHints"))
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -128,3 +128,4 @@
|
|||
{"ts":"2026-05-17T22:41:56Z","type":"implementation","status":"ok","note":"Persona webhook delivery now supports configured per-channel webhook URLs and preserves Discord failure details in the parent rumination runner; live probe confirms current #general blocker is Discord 403 Missing Permissions unless Manage Webhooks is granted or DISCORD_PERSONA_WEBHOOK_URL_113786069023064068 is configured."}
|
||||
{"ts":"2026-05-17T23:50:11Z","type":"implementation","status":"ok","note":"Persona webhook delivery now resolves avatar URLs from per-persona env keys such as DISCORD_PERSONA_AVATAR_URL_VOID, falling back to DISCORD_PERSONA_AVATAR_URL; live Void probe used the bot's Discord CDN avatar without an explicit operation-level avatar URL."}
|
||||
{"ts":"2026-05-17T23:59:13Z","type":"implementation","status":"ok","note":"Repeated logo reply at 2026-05-17T23:56Z was caused by two queued candidates sharing the same Discord reply target after webhook delivery surgery. Marking one candidate spoken now retires queued sibling candidates for the same channel/reply target; speech fixture covers the duplicate-target case."}
|
||||
{"ts":"2026-05-18T02:42:56Z","type":"implementation","status":"ok","note":"Repo activity projection now includes bounded contentHints from changed text files, so rumination can notice facts like recent GameCult blog posts carrying author: Void and then choose source retrieval to read them. This replaces the rejected self-authored-blog side channel with a broader repo-change visibility surface; answered open cases are also filtered from prompt-facing openCases."}
|
||||
|
|
|
|||
Loading…
Reference in a new issue