From cf526a7c783a9ce2ac3bcc644d3eed99dcb332cd Mon Sep 17 00:00:00 2001 From: Meta Vens Date: Mon, 13 Jul 2026 12:42:58 +0100 Subject: [PATCH] Resolve Face source repos by canonical upstream --- scripts/export-recent-repo-activity.mjs | 37 ++++++++++++++++++++++++- scripts/run-repo-face-heartbeats.ts | 13 ++++++--- 2 files changed, 45 insertions(+), 5 deletions(-) diff --git a/scripts/export-recent-repo-activity.mjs b/scripts/export-recent-repo-activity.mjs index 48a81f7..0e40b7a 100644 --- a/scripts/export-recent-repo-activity.mjs +++ b/scripts/export-recent-repo-activity.mjs @@ -244,18 +244,53 @@ function readTrackedRepoNames(archivePath, requestedRepoNames) { function readAvailableRepos(sourceRepoRoot) { const lookup = new Map(); + const canonicalCandidates = new Map(); for (const entry of readdirSync(sourceRepoRoot, { withFileTypes: true })) { if (!entry.isDirectory()) { continue; } - lookup.set(entry.name.toLowerCase(), resolve(sourceRepoRoot, entry.name)); + const repoPath = resolve(sourceRepoRoot, entry.name); + lookup.set(entry.name.toLowerCase(), repoPath); + + const canonicalRepoName = readCanonicalGameCultRepoName(repoPath); + if (!canonicalRepoName) { + continue; + } + + const key = canonicalRepoName.toLowerCase(); + const candidates = canonicalCandidates.get(key) ?? []; + candidates.push({ localRepoName: entry.name, repoPath, canonicalRepoName }); + canonicalCandidates.set(key, candidates); + } + + for (const [key, candidates] of canonicalCandidates) { + candidates.sort((left, right) => { + const leftExact = left.localRepoName.toLowerCase() === left.canonicalRepoName.toLowerCase() ? 0 : 1; + const rightExact = right.localRepoName.toLowerCase() === right.canonicalRepoName.toLowerCase() ? 0 : 1; + return leftExact - rightExact || left.localRepoName.localeCompare(right.localRepoName); + }); + lookup.set(key, candidates[0].repoPath); } return lookup; } +function readCanonicalGameCultRepoName(repoPath) { + try { + const originUrl = execFileSync( + "git", + ["-C", repoPath, "remote", "get-url", "origin"], + { encoding: "utf8", windowsHide: true, stdio: ["ignore", "pipe", "ignore"] }, + ).trim(); + const match = originUrl.match(/github\.com[:/]GameCult\/([^/]+?)(?:\.git)?$/i); + return match?.[1] || null; + } catch { + return null; + } +} + function inspectRepoActivity({ repoName, repoPath, sinceIso, maxCommits, cursorEntry }) { if (!repoPath) { return { diff --git a/scripts/run-repo-face-heartbeats.ts b/scripts/run-repo-face-heartbeats.ts index 07d1f9e..3ed584e 100644 --- a/scripts/run-repo-face-heartbeats.ts +++ b/scripts/run-repo-face-heartbeats.ts @@ -4773,13 +4773,18 @@ function renderRepoFaceRepoActivitySurface( identity: RepoDiscordIdentity, config: ReturnType, ): string { + const sourceRepoName = getRepoFaceSourceRepoName(identity); + if (!sourceRepoName) { + return "- This Persona has no source repository configured; no repo activity was requested."; + } + const statePath = resolveRepoFaceStatePath(identity, config.storageRoot); const result = spawnSync( process.execPath, [ resolve("scripts", "export-recent-repo-activity.mjs"), "--repos", - identity.repoName, + sourceRepoName, "--state-path", statePath, "--read-only", @@ -4800,7 +4805,7 @@ function renderRepoFaceRepoActivitySurface( if (result.status !== 0) { const detail = `${result.stdout}\n${result.stderr}`.trim().slice(-600); return [ - `- Recent ${identity.repoName} activity could not be read for this turn.`, + `- Recent ${sourceRepoName} activity could not be read for this turn.`, detail ? `- Reader error: ${collapseWhitespace(detail, 500)}` : "- Reader error: no diagnostic output.", "- Do not claim current repo state from stale memory; use source/history tools before making fresh claims.", ].join("\n"); @@ -4809,10 +4814,10 @@ function renderRepoFaceRepoActivitySurface( try { const parsed = JSON.parse(result.stdout) as { digest?: unknown }; const digest = typeof parsed.digest === "string" ? parsed.digest.trim() : ""; - return digest || `- No recent ${identity.repoName} activity was reported.`; + return digest || `- No recent ${sourceRepoName} activity was reported.`; } catch { return [ - `- Recent ${identity.repoName} activity output was not parseable.`, + `- Recent ${sourceRepoName} activity output was not parseable.`, `- Raw output: ${collapseWhitespace(result.stdout, 500)}`, "- Do not claim current repo state from stale memory; use source/history tools before making fresh claims.", ].join("\n");