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: 12 additions & 4 deletions packages/action/dist/index.mjs
Original file line number Diff line number Diff line change
Expand Up @@ -976,6 +976,7 @@ var PRESENTATION_CODE_PENALTY = 8;
var TYPE_DECLARATION_PENALTY = 4;
var BACKUP_COPY_PENALTY = 10;
var BUNDLED_OUTPUT_PENALTY = 12;
var GENERATED_TWIN_REASON = "generated build artifact; maintained source counterpart exists";
var BUNDLED_LINE_LENGTH = 400;
var MIN_BUNDLE_SAMPLE_BYTES = 2e3;
var BUNDLE_MARKERS = [
Expand Down Expand Up @@ -1054,6 +1055,9 @@ function rankContextFiles(repo, input, limit = DEFAULT_CONTEXT_FILE_LIMIT, minSc
score += EXPLICIT_PATH_BOOST;
reasons.push("explicitly named in the task");
}
if (isGeneratedPath(file.path) && maintainedStems.has(moduleStem(file.path))) {
reasons.push(GENERATED_TWIN_REASON);
}
const pathTokens = tokenizePath(file.path);
const pathOverlap = [...pathTokens].filter((token) => taskTokens.has(token));
if (pathOverlap.length > 0) {
Expand Down Expand Up @@ -1123,7 +1127,7 @@ function rankContextFiles(repo, input, limit = DEFAULT_CONTEXT_FILE_LIMIT, minSc
score -= EXAMPLE_CODE_PENALTY;
reasons.push("example or demo code deprioritized for an implementation task");
}
if (file.kind === "code" && isPresentationCodePath(file.path) && !taskTargetsPresentation && !taskTargetsExamples && !isChanged && !mentionedPaths.has(file.path)) {
if (isPresentationSurfacePath(file.path) && !taskTargetsPresentation && !taskTargetsExamples && !isChanged && !mentionedPaths.has(file.path)) {
score -= PRESENTATION_CODE_PENALTY;
reasons.push("presentation or demo surface deprioritized for a non-UI implementation task");
}
Expand Down Expand Up @@ -1208,7 +1212,8 @@ function confidenceForEntry(entry, grounding, clustered, shape) {
if (entry.isChanged) {
return "high";
}
if (entry.reasons.includes("explicitly named in the task") && shape.position === 0 && !shape.leadIsContested) {
const hasMaintainedSourceTwin = entry.reasons.includes(GENERATED_TWIN_REASON);
if (entry.reasons.includes("explicitly named in the task") && shape.position === 0 && !shape.leadIsContested && !hasMaintainedSourceTwin) {
return "high";
}
let confidence = entry.score >= 14 ? "high" : entry.score >= 8 ? "medium" : "low";
Expand All @@ -1219,6 +1224,9 @@ function confidenceForEntry(entry, grounding, clustered, shape) {
if (shape.position === 0 && shape.leadIsContested) {
confidence = capConfidence(confidence, "medium");
}
if (hasMaintainedSourceTwin) {
confidence = capConfidence(confidence, "medium");
}
const supportedIdentifierCount = grounding.identifiers.filter((identifier) => identifier.status === "exact-definition" || identifier.status === "exact-text" || identifier.status === "partial-definition").length;
if (grounding.unresolvedIdentifiers.length > 0) {
if (supportedIdentifierCount === 0) {
Expand Down Expand Up @@ -1302,9 +1310,9 @@ function isAuxiliaryCodePath(path) {
const stem = (parts.at(-1) ?? "").replace(/\.[^.]+$/, "").toLowerCase();
return parts.slice(0, -1).some((segment) => AUXILIARY_CODE_DIRS.has(segment.toLowerCase())) || /^(?:demo|example|sample)(?:[-_.]|$)/.test(stem);
}
function isPresentationCodePath(path) {
function isPresentationSurfacePath(path) {
const name = path.split("/").at(-1)?.toLowerCase() ?? "";
return /^(?:page|layout|demo|sample-repo)\.[cm]?[jt]sx?$/.test(name);
return /^(?:page|layout|demo|sample-repo)\.[cm]?[jt]sx?$/.test(name) || /\.(?:css|less|sass|scss)$/.test(name);
}
function tokenizeFileContent(text) {
const tokens = tokenizeText(text);
Expand Down
24 changes: 19 additions & 5 deletions packages/core/src/rank.ts
Original file line number Diff line number Diff line change
Expand Up @@ -30,6 +30,7 @@ const PRESENTATION_CODE_PENALTY = 8;
const TYPE_DECLARATION_PENALTY = 4;
const BACKUP_COPY_PENALTY = 10;
const BUNDLED_OUTPUT_PENALTY = 12;
const GENERATED_TWIN_REASON = "generated build artifact; maintained source counterpart exists";
// Bundlers strip newlines; people do not. A file averaging hundreds of characters per
// line is machine output, whatever directory it sits in. Repositories commit these —
// Next.js keeps pre-bundled dependencies under `src/compiled/` — and because they have
Expand Down Expand Up @@ -162,6 +163,10 @@ export function rankContextFiles(
reasons.push("explicitly named in the task");
}

if (isGeneratedPath(file.path) && maintainedStems.has(moduleStem(file.path))) {
reasons.push(GENERATED_TWIN_REASON);
}

const pathTokens = tokenizePath(file.path);
const pathOverlap = [...pathTokens].filter((token) => taskTokens.has(token));
if (pathOverlap.length > 0) {
Expand Down Expand Up @@ -270,8 +275,7 @@ export function rankContextFiles(
}

if (
file.kind === "code" &&
isPresentationCodePath(file.path) &&
isPresentationSurfacePath(file.path) &&
!taskTargetsPresentation &&
!taskTargetsExamples &&
!isChanged &&
Expand Down Expand Up @@ -413,7 +417,13 @@ function confidenceForEntry(
if (entry.isChanged) {
return "high";
}
if (entry.reasons.includes("explicitly named in the task") && shape.position === 0 && !shape.leadIsContested) {
const hasMaintainedSourceTwin = entry.reasons.includes(GENERATED_TWIN_REASON);
if (
entry.reasons.includes("explicitly named in the task") &&
shape.position === 0 &&
!shape.leadIsContested &&
!hasMaintainedSourceTwin
) {
return "high";
}

Expand All @@ -433,6 +443,9 @@ function confidenceForEntry(
if (shape.position === 0 && shape.leadIsContested) {
confidence = capConfidence(confidence, "medium");
}
if (hasMaintainedSourceTwin) {
confidence = capConfidence(confidence, "medium");
}
const supportedIdentifierCount = grounding.identifiers.filter((identifier) =>
identifier.status === "exact-definition" ||
identifier.status === "exact-text" ||
Expand Down Expand Up @@ -545,9 +558,10 @@ function isAuxiliaryCodePath(path: string): boolean {
/^(?:demo|example|sample)(?:[-_.]|$)/.test(stem);
}

function isPresentationCodePath(path: string): boolean {
function isPresentationSurfacePath(path: string): boolean {
const name = path.split("/").at(-1)?.toLowerCase() ?? "";
return /^(?:page|layout|demo|sample-repo)\.[cm]?[jt]sx?$/.test(name);
return /^(?:page|layout|demo|sample-repo)\.[cm]?[jt]sx?$/.test(name) ||
/\.(?:css|less|sass|scss)$/.test(name);
}

function tokenizeFileContent(text: string): Set<string> {
Expand Down
98 changes: 97 additions & 1 deletion packages/core/test/rank.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -546,6 +546,60 @@ describe("rankContextFiles", () => {
.not.toContain("type declaration deprioritized for a runtime task");
});

it("keeps stylesheet symptom words below implementation unless the task targets presentation", () => {
const repo: RepoMap = {
root: "/repo",
packageScripts: [],
changedFiles: [],
diffText: "",
packageManager: "npm",
diagnostics: [],
files: [
{
path: "src/checkout.ts",
extension: ".ts",
sizeBytes: 100,
isSource: true,
isTest: false,
kind: "code",
textSample: "export function applyDiscount(cart: Cart) { return cart.total; }"
},
{
path: "styles/discount.css",
extension: ".css",
sizeBytes: 100,
isSource: true,
isTest: false,
kind: "code",
textSample: ".discount-banner .discount-total .discount-cart .discount { color: red; }"
},
{
path: "src/copy.json",
extension: ".json",
sizeBytes: 100,
isSource: true,
isTest: false,
kind: "config",
textSample: "{ \"discount\": \"Discount total on cart\" }"
}
]
};

const implementationTask = rankContextFiles(repo, {
issueText: "discount total on cart is wrong"
}, 8, 0);
expect(implementationTask[0]?.path).toBe("src/checkout.ts");
expect(implementationTask.find((file) => file.path === "styles/discount.css")?.reasons)
.toContain("presentation or demo surface deprioritized for a non-UI implementation task");

const presentationTask = rankContextFiles(repo, {
issueText: "discount banner CSS layout is wrong on the cart page"
});
expect(presentationTask[0]?.path).toBe("styles/discount.css");
expect(presentationTask[0]?.reasons)
.not.toContain("presentation or demo surface deprioritized for a non-UI implementation task");
});

it("keeps documentation noise below matching code unless the task targets docs", () => {
const repo: RepoMap = {
root: "/repo",
Expand Down Expand Up @@ -716,7 +770,49 @@ describe("rankContextFiles", () => {
issueText: "dist/color-support.js is stale and disagrees with the source"
});

expect(ranked.map((file) => file.path)).toContain("dist/color-support.js");
const artifact = ranked.find((file) => file.path === "dist/color-support.js");
expect(artifact?.reasons).toContain("explicitly named in the task");
expect(artifact?.reasons).toContain("generated build artifact; maintained source counterpart exists");
expect(artifact?.confidence).toBe("medium");
});

it("caps confidence when an explicitly named generated path has a maintained twin", () => {
const repo: RepoMap = {
root: "/repo",
packageScripts: [],
changedFiles: [],
diffText: "",
packageManager: "npm",
diagnostics: [],
files: [
{
path: "packages/action/src/index.ts",
extension: ".ts",
sizeBytes: 100,
isSource: true,
isTest: false,
kind: "code",
textSample: "export function postComment(marker: string) { return marker; }"
},
{
path: "packages/action/dist/index.mjs",
extension: ".mjs",
sizeBytes: 100,
isSource: true,
isTest: false,
kind: "code",
textSample: "export function postComment(marker) { return marker; }"
}
]
};

const ranked = rankContextFiles(repo, {
issueText: "the bundle in packages/action/dist/index.mjs mishandles the comment marker"
});

expect(ranked[0]?.path).toBe("packages/action/dist/index.mjs");
expect(ranked[0]?.confidence).toBe("medium");
expect(ranked[0]?.reasons).toContain("generated build artifact; maintained source counterpart exists");
});

it("still credits a task term that most of a focused repository mentions", () => {
Expand Down