Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
16 changes: 15 additions & 1 deletion src/github/commands.ts
Original file line number Diff line number Diff line change
Expand Up @@ -744,6 +744,8 @@ function askSourcesFromContextSnapshot(snapshot: AgentRunBundle["contextSnapshot
return sources;
}

const PRIVATE_ASK_ACTION_EVIDENCE_SOURCES = new Set(["repo_decision"]);

function askSourcesFromActionEvidence(action: AgentActionRecord): AskContributingSource[] {
const evidence = readRecord(action.payload.recommendationEvidence);
if (!evidence || !Array.isArray(evidence.sources)) return [];
Expand All @@ -752,18 +754,30 @@ function askSourcesFromActionEvidence(action: AgentActionRecord): AskContributin
if (!raw || typeof raw !== "object" || Array.isArray(raw)) return null;
const source = raw as Record<string, unknown>;
const name = typeof source.name === "string" ? source.name : "connected_source";
if (PRIVATE_ASK_ACTION_EVIDENCE_SOURCES.has(name)) return null;
return {
key: name,
label: askSourceLabel(name),
origin: name,
generatedAt: typeof source.generatedAt === "string" ? source.generatedAt : null,
freshness: typeof source.freshness === "string" ? source.freshness : "unknown",
detail: typeof source.summary === "string" ? source.summary : "Connected recommendation evidence source.",
detail: publicActionEvidenceSourceDetail(name),
};
})
.filter((entry): entry is AskContributingSource => entry !== null);
}

function publicActionEvidenceSourceDetail(name: string): string {
const details: Record<string, string> = {
contributor_decision_pack: "Contributor decision-pack metadata was available for this cached agent run.",
official_contributor_stats: "Official contributor statistics metadata was available for this cached agent run.",
repo_outcome_history: "Repo outcome history metadata was available for this cached agent run.",
aggregate_outcome_quality: "Aggregate outcome-quality metadata was available for this cached agent run.",
open_pr_monitor: "Cached open PR and issue queue metadata was available for this cached agent run.",
};
return details[name] ?? "Connected recommendation evidence metadata was available for this cached agent run.";
}

function formatAskCitation(source: AskContributingSource): string {
const header = `Source: ${source.label}; origin: ${source.origin}; freshness: ${source.freshness}`;
const observed = source.generatedAt ? ` as of ${source.generatedAt}` : "";
Expand Down
62 changes: 62 additions & 0 deletions test/unit/github-commands.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -1549,6 +1549,68 @@ describe("ask citation helpers", () => {
expect(sections.join("\n")).toContain("Try @gittensory ask again shortly");
});

it("does not publish private repo decision ranking evidence in ask citations", () => {
const bundle = askCitedBundle({
actions: [
{
id: "ask-private-decision-action",
runId: "run-ask-cited",
actionType: "choose_next_work",
status: "recommended",
recommendation: "recommendation",
why: [],
blockedBy: [],
targetRepoFullName: "owner/private-repo",
publicSafeSummary: "Run local branch preflight first.",
approvalRequired: true,
safetyClass: "private",
payload: {
recommendationEvidence: {
confidence: "high",
sourceSummary: "Decision pack evidence",
freshness: "fresh",
sources: [
{
name: "repo_decision",
source: "decision_pack",
generatedAt: "2026-06-01T12:00:00.000Z",
freshness: "fresh",
summary: "owner/private-repo ranked pursue_now at priority 0.87321.",
},
{
name: "open_pr_monitor",
source: "github_cache",
generatedAt: "2026-06-01T12:00:00.000Z",
freshness: "fresh",
summary: "Open PR monitor queue metadata.",
},
],
},
},
},
],
contextSnapshots: [],
});

const sources = githubCommandsInternals.collectAskContributingSources(bundle);
expect(sources.some((source) => source.origin === "repo_decision")).toBe(false);
expect(sources.find((source) => source.origin === "open_pr_monitor")?.detail).toBe(
"Cached open PR and issue queue metadata was available for this cached agent run.",
);

const comment = buildPublicAgentCommandComment({
command: parseGittensoryMentionCommand("@gittensory ask what should I do next?")!,
repo: null,
issue: { number: 44, title: "PR", state: "open", pull_request: {} },
pullRequest: null,
actorKind: "author",
bundle,
});
expect(comment).toContain("origin: open_pr_monitor");
expect(comment).not.toMatch(/ranked|pursue_now|priority 0\.87321/i);
expect(comment).not.toContain("owner/private-repo ranked");
});

it("collects connected-source metadata and formats concrete citations", () => {
const sources = githubCommandsInternals.collectAskContributingSources({
run: completedRun("run-ask-internals"),
Expand Down