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
11 changes: 6 additions & 5 deletions src/api/routes.ts
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
import { Hono, type Context } from "hono";

Check notice on line 1 in src/api/routes.ts

View check run for this annotation

Deleted GitHub App / Gittensory Context

Possible duplicate overlap

Titles/paths share 6 meaningful terms.

Check notice on line 1 in src/api/routes.ts

View check run for this annotation

Deleted GitHub App / Gittensory Context

Issue discovery is disabled for this repo

This repo is configured for direct contribution review rather than issue-discovery flow.

Check notice on line 1 in src/api/routes.ts

View check run for this annotation

Deleted GitHub App / Gittensory Context

PR author has maintainer association

This PR appears to come from a maintainer-associated account.
import { z } from "zod";
import { analyzePRQueue, type AuthorRole, type ChecksStatus } from "../queue-intelligence";
import { completeGitHubWebOAuth, createSessionFromGitHubToken, pollGitHubDeviceFlow, startGitHubDeviceFlow, startGitHubWebOAuth } from "../auth/github-oauth";
Expand All @@ -23,6 +23,7 @@
} from "../auth/security";
import { normalizeGittBountySnapshot } from "../bounties/ingest";
import { DEFAULT_COMMAND_AUTHORIZATION_POLICY, normalizeCommandAuthorizationPolicy } from "../settings/command-authorization";
import { SCENARIO_MAX_BRANCH_REF_CHARS, SCENARIO_MAX_LINKED_ISSUE_NUMBERS, SCENARIO_MAX_REPO_FULL_NAME_CHARS } from "../scenarios/input-model";
import {
countOpenIssues,
countOpenPullRequests,
Expand Down Expand Up @@ -410,18 +411,18 @@
const localBranchAnalysisSchema = z
.object({
login: z.string().min(1).max(MAX_LOCAL_BRANCH_REF_CHARS),
repoFullName: z.string().min(3).max(MAX_LOCAL_BRANCH_REF_CHARS),
baseRef: z.string().min(1).max(MAX_LOCAL_BRANCH_REF_CHARS).optional(),
headRef: z.string().min(1).max(MAX_LOCAL_BRANCH_REF_CHARS).optional(),
branchName: z.string().min(1).max(MAX_LOCAL_BRANCH_REF_CHARS).optional(),
repoFullName: z.string().min(3).max(SCENARIO_MAX_REPO_FULL_NAME_CHARS),
baseRef: z.string().min(1).max(SCENARIO_MAX_BRANCH_REF_CHARS).optional(),
headRef: z.string().min(1).max(SCENARIO_MAX_BRANCH_REF_CHARS).optional(),
branchName: z.string().min(1).max(SCENARIO_MAX_BRANCH_REF_CHARS).optional(),
baseSha: z.string().min(1).max(MAX_LOCAL_BRANCH_REF_CHARS).optional(),
headSha: z.string().min(1).max(MAX_LOCAL_BRANCH_REF_CHARS).optional(),
mergeBaseSha: z.string().min(1).max(MAX_LOCAL_BRANCH_REF_CHARS).optional(),
remoteTrackingSha: z.string().min(1).max(MAX_LOCAL_BRANCH_REF_CHARS).optional(),
commitMessages: z.array(z.string().max(MAX_LOCAL_BRANCH_TEXT_CHARS)).max(30).optional(),
changedFiles: z.array(localBranchChangedFileSchema).max(500).optional(),
validation: z.array(localBranchValidationSchema).max(50).optional(),
linkedIssues: z.array(z.number().int().positive()).optional(),
linkedIssues: z.array(z.number().int().positive()).max(SCENARIO_MAX_LINKED_ISSUE_NUMBERS).optional(),
labels: z.array(z.string().min(1).max(MAX_LOCAL_BRANCH_REF_CHARS)).max(50).optional(),
title: z.string().min(1).max(MAX_LOCAL_BRANCH_REF_CHARS).optional(),
body: z.string().max(MAX_LOCAL_BRANCH_TEXT_CHARS).optional(),
Expand Down
13 changes: 7 additions & 6 deletions src/mcp/server.ts
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
import { createMcpHandler } from "agents/mcp";

Check notice on line 1 in src/mcp/server.ts

View check run for this annotation

Deleted GitHub App / Gittensory Context

Possible duplicate overlap

Titles/paths share 6 meaningful terms.

Check notice on line 1 in src/mcp/server.ts

View check run for this annotation

Deleted GitHub App / Gittensory Context

Issue discovery is disabled for this repo

This repo is configured for direct contribution review rather than issue-discovery flow.

Check notice on line 1 in src/mcp/server.ts

View check run for this annotation

Deleted GitHub App / Gittensory Context

PR author has maintainer association

This PR appears to come from a maintainer-associated account.
import type { Context } from "hono";
import { McpServer } from "@modelcontextprotocol/sdk/server/mcp.js";
import type { RequestHandlerExtra } from "@modelcontextprotocol/sdk/shared/protocol.js";
Expand Down Expand Up @@ -79,6 +79,7 @@
import { loadRepoFocusManifest } from "../signals/focus-manifest-loader";
import { buildRepoDataQuality } from "../signals/data-quality";
import { PREFLIGHT_LIMITS } from "../signals/preflight-limits";
import { SCENARIO_MAX_BRANCH_REF_CHARS, SCENARIO_MAX_LINKED_ISSUE_NUMBERS, SCENARIO_MAX_REPO_FULL_NAME_CHARS } from "../scenarios/input-model";
import { loadUpstreamStatus } from "../upstream/ruleset";

type AppContext = Context<{ Bindings: Env }>;
Expand Down Expand Up @@ -162,11 +163,11 @@
};

const localBranchAnalysisShape = {
login: z.string().min(1),
repoFullName: z.string().min(3),
baseRef: z.string().min(1).optional(),
headRef: z.string().min(1).optional(),
branchName: z.string().min(1).optional(),
login: z.string().min(1).max(SCENARIO_MAX_BRANCH_REF_CHARS),
repoFullName: z.string().min(3).max(SCENARIO_MAX_REPO_FULL_NAME_CHARS),
baseRef: z.string().min(1).max(SCENARIO_MAX_BRANCH_REF_CHARS).optional(),
headRef: z.string().min(1).max(SCENARIO_MAX_BRANCH_REF_CHARS).optional(),
branchName: z.string().min(1).max(SCENARIO_MAX_BRANCH_REF_CHARS).optional(),
baseSha: z.string().min(1).optional(),
headSha: z.string().min(1).optional(),
mergeBaseSha: z.string().min(1).optional(),
Expand Down Expand Up @@ -201,7 +202,7 @@
)
.max(50)
.optional(),
linkedIssues: z.array(z.number().int().positive()).optional(),
linkedIssues: z.array(z.number().int().positive()).max(SCENARIO_MAX_LINKED_ISSUE_NUMBERS).optional(),
labels: z.array(z.string()).optional(),
title: z.string().min(1).optional(),
body: z.string().optional(),
Expand Down
58 changes: 43 additions & 15 deletions src/scenarios/input-model.ts
Original file line number Diff line number Diff line change
@@ -1,7 +1,11 @@
import { z } from "zod";

Check notice on line 1 in src/scenarios/input-model.ts

View check run for this annotation

Deleted GitHub App / Gittensory Context

Possible duplicate overlap

Titles/paths share 6 meaningful terms.

Check notice on line 1 in src/scenarios/input-model.ts

View check run for this annotation

Deleted GitHub App / Gittensory Context

Issue discovery is disabled for this repo

This repo is configured for direct contribution review rather than issue-discovery flow.

Check notice on line 1 in src/scenarios/input-model.ts

View check run for this annotation

Deleted GitHub App / Gittensory Context

PR author has maintainer association

This PR appears to come from a maintainer-associated account.
import { sanitizePublicComment } from "../github/commands";

export const SCENARIO_INPUT_VERSION = 1 as const;
export const SCENARIO_MAX_REPO_FULL_NAME_CHARS = 200;
export const SCENARIO_MAX_BRANCH_REF_CHARS = 200;
export const SCENARIO_MAX_LINKED_ISSUE_NUMBERS = 50;
export const SCENARIO_MAX_SIGNAL_DETAIL_CHARS = 2000;

export const scenarioInputKinds = ["fact", "assumption", "estimate", "unavailable"] as const;
export type ScenarioInputKind = (typeof scenarioInputKinds)[number];
Expand Down Expand Up @@ -36,7 +40,7 @@
id: z.string().min(1).max(120),
kind: z.enum(scenarioInputKinds),
label: z.string().min(1).max(200),
detail: z.string().min(1).max(2000),
detail: z.string().min(1).max(SCENARIO_MAX_SIGNAL_DETAIL_CHARS),
source: z.enum(scenarioSignalSources),
})
.strict();
Expand All @@ -45,7 +49,7 @@

const scenarioRepoConfigSchema = z
.object({
repoFullName: z.string().min(3).max(200),
repoFullName: z.string().min(3).max(SCENARIO_MAX_REPO_FULL_NAME_CHARS),
registered: z.boolean().optional(),
maintainerLane: z.boolean().optional(),
})
Expand All @@ -56,7 +60,7 @@
const scenarioIssueStateSchema = z
.object({
openIssueCount: z.number().int().min(0).optional(),
linkedIssueNumbers: z.array(z.number().int().positive()).max(50).optional(),
linkedIssueNumbers: z.array(z.number().int().positive()).max(SCENARIO_MAX_LINKED_ISSUE_NUMBERS).optional(),
})
.strict();

Expand All @@ -75,9 +79,9 @@

const scenarioBranchStateSchema = z
.object({
branchName: z.string().min(1).max(200).optional(),
baseRef: z.string().min(1).max(200).optional(),
headRef: z.string().min(1).max(200).optional(),
branchName: z.string().min(1).max(SCENARIO_MAX_BRANCH_REF_CHARS).optional(),
baseRef: z.string().min(1).max(SCENARIO_MAX_BRANCH_REF_CHARS).optional(),
headRef: z.string().min(1).max(SCENARIO_MAX_BRANCH_REF_CHARS).optional(),
pendingCommitCount: z.number().int().min(0).optional(),
changedFileCount: z.number().int().min(0).optional(),
eligibilityStatus: z.enum(["eligible", "ineligible", "unknown"]).optional(),
Expand Down Expand Up @@ -219,40 +223,49 @@
scenarioNotes?: string[];
eligibilityStatus?: "eligible" | "ineligible" | "unknown";
}): AgentScenarioInput {
const login = trimScenarioText(args.login, SCENARIO_MAX_BRANCH_REF_CHARS);
const repoFullName = trimScenarioText(args.repoFullName, SCENARIO_MAX_REPO_FULL_NAME_CHARS);
const branchName = optionalScenarioText(args.branchName, SCENARIO_MAX_BRANCH_REF_CHARS);
const baseRef = optionalScenarioText(args.baseRef, SCENARIO_MAX_BRANCH_REF_CHARS);
const linkedIssues = args.linkedIssues?.slice(0, SCENARIO_MAX_LINKED_ISSUE_NUMBERS);
const scenarioNotes = args.scenarioNotes
?.map((note) => trimScenarioText(note, SCENARIO_MAX_SIGNAL_DETAIL_CHARS))
.filter((note) => note.length > 0);

const facts: ScenarioSignalEntry[] = [
createScenarioSignalEntry({
id: "actor",
kind: "fact",
label: "Contributor",
detail: `Planning scenario for ${args.login}.`,
detail: `Planning scenario for ${login}.`,
source: "github_observed",
}),
createScenarioSignalEntry({
id: "repo",
kind: "fact",
label: "Repository",
detail: `Repo context is ${args.repoFullName}.`,
detail: `Repo context is ${repoFullName}.`,
source: "local_metadata",
}),
];
if (args.branchName) {
if (branchName) {
facts.push(
createScenarioSignalEntry({
id: "branch",
kind: "fact",
label: "Branch",
detail: `Active branch ${args.branchName}${args.baseRef ? ` against ${args.baseRef}` : ""}.`,
detail: `Active branch ${branchName}${baseRef ? ` against ${baseRef}` : ""}.`,
source: "local_metadata",
}),
);
}
const assumptions =
args.scenarioNotes?.map((note, index) =>
scenarioNotes?.map((note, index) =>
createScenarioSignalEntry({
id: `assumption_${index + 1}`,
kind: "assumption",
label: "Caller assumption",
detail: note.trim(),
detail: note,
source: "user_supplied",
}),
) ?? [];
Expand All @@ -268,12 +281,17 @@
}),
);
}
const branchState = compactBranchState(args);
const branchState = compactBranchState({
...(branchName ? { branchName } : {}),
...(baseRef ? { baseRef } : {}),
...(args.changedFileCount !== undefined ? { changedFileCount: args.changedFileCount } : {}),
...(args.eligibilityStatus ? { eligibilityStatus: args.eligibilityStatus } : {}),
});
return buildScenarioInput({
scenarioType: args.scenarioType,
repoFullName: args.repoFullName,
repoFullName,
...(branchState ? { branchState } : {}),
...(args.linkedIssues?.length ? { issueState: { linkedIssueNumbers: args.linkedIssues } } : {}),
...(linkedIssues?.length ? { issueState: { linkedIssueNumbers: linkedIssues } } : {}),
facts,
assumptions,
unavailableSignals,
Expand Down Expand Up @@ -339,6 +357,16 @@
}
}

function trimScenarioText(value: string, maxLength: number): string {
return value.trim().slice(0, maxLength);
}

function optionalScenarioText(value: string | undefined, maxLength: number): string | undefined {
if (!value) return undefined;
const trimmed = trimScenarioText(value, maxLength);
return trimmed.length > 0 ? trimmed : undefined;
}

function sortEntries(entries: ScenarioSignalEntry[]): ScenarioSignalEntry[] {
return [...entries].sort((left, right) => left.id.localeCompare(right.id));
}
Expand Down
25 changes: 25 additions & 0 deletions test/unit/local-branch.test.ts
Original file line number Diff line number Diff line change
@@ -1,9 +1,10 @@
import { execFileSync } from "node:child_process";

Check notice on line 1 in test/unit/local-branch.test.ts

View check run for this annotation

Deleted GitHub App / Gittensory Context

Possible duplicate overlap

Titles/paths share 6 meaningful terms.

Check notice on line 1 in test/unit/local-branch.test.ts

View check run for this annotation

Deleted GitHub App / Gittensory Context

Issue discovery is disabled for this repo

This repo is configured for direct contribution review rather than issue-discovery flow.

Check notice on line 1 in test/unit/local-branch.test.ts

View check run for this annotation

Deleted GitHub App / Gittensory Context

PR author has maintainer association

This PR appears to come from a maintainer-associated account.
import { mkdtempSync, mkdirSync, rmSync, writeFileSync } from "node:fs";
import { tmpdir } from "node:os";
import { join } from "node:path";
import { pathToFileURL } from "node:url";
import { afterEach, describe, expect, it } from "vitest";
import { SCENARIO_MAX_BRANCH_REF_CHARS, SCENARIO_MAX_LINKED_ISSUE_NUMBERS } from "../../src/scenarios/input-model";
import { buildLocalBranchAnalysis, findCurrentBranchPullRequest } from "../../src/signals/local-branch";
import { MAX_LOCAL_SCORER_WARNING_CHARS, MAX_LOCAL_SCORER_WARNING_COUNT } from "../../src/signals/local-scorer-diagnostics";
import type { ContributorOutcomeHistory, ContributorProfile, ContributorScoringProfile, IssueQualityReport } from "../../src/signals/engine";
Expand Down Expand Up @@ -1593,6 +1594,30 @@
expect(analysis.scenarioSummary.dataClassification.facts).toContain("Branch");
});

it("does not throw while summarizing oversized local branch metadata", () => {
const analysis = buildLocalBranchAnalysis({
input: {
login: "oktofeesh1",
repoFullName: repo.fullName,
branchName: "b".repeat(SCENARIO_MAX_BRANCH_REF_CHARS + 1),
baseRef: "m".repeat(SCENARIO_MAX_BRANCH_REF_CHARS + 1),
linkedIssues: Array.from({ length: SCENARIO_MAX_LINKED_ISSUE_NUMBERS + 1 }, (_, index) => index + 1),
changedFiles: [{ path: "src/util.ts", additions: 20, deletions: 1, status: "modified" }],
localScorer: { mode: "external_command", sourceTokenScore: 35, totalTokenScore: 55, sourceLines: 30 },
},
repo,
issues: [],
pullRequests: [],
profile,
outcomeHistory,
scoringSnapshot,
scoringProfile,
});

expect(analysis.scenarioSummary.dataClassification.facts).toContain("Branch");
expect(analysis.branchName).toHaveLength(SCENARIO_MAX_BRANCH_REF_CHARS + 1);
});

it("populates scenarioSummary.eligibilityNotes from the derived eligibility plan", () => {
const analysis = buildLocalBranchAnalysis({
input: {
Expand Down
22 changes: 22 additions & 0 deletions test/unit/scenario-input-model.test.ts
Original file line number Diff line number Diff line change
@@ -1,5 +1,9 @@
import { describe, expect, it } from "vitest";

Check notice on line 1 in test/unit/scenario-input-model.test.ts

View check run for this annotation

Deleted GitHub App / Gittensory Context

Possible duplicate overlap

Titles/paths share 6 meaningful terms.

Check notice on line 1 in test/unit/scenario-input-model.test.ts

View check run for this annotation

Deleted GitHub App / Gittensory Context

Issue discovery is disabled for this repo

This repo is configured for direct contribution review rather than issue-discovery flow.

Check notice on line 1 in test/unit/scenario-input-model.test.ts

View check run for this annotation

Deleted GitHub App / Gittensory Context

PR author has maintainer association

This PR appears to come from a maintainer-associated account.
import {
SCENARIO_MAX_BRANCH_REF_CHARS,
SCENARIO_MAX_LINKED_ISSUE_NUMBERS,
SCENARIO_MAX_REPO_FULL_NAME_CHARS,
SCENARIO_MAX_SIGNAL_DETAIL_CHARS,
assertScenarioLocalBranchInputSafe,
buildScenarioInput,
createScenarioSignalEntry,
Expand Down Expand Up @@ -264,6 +268,24 @@
expect(input.unavailableSignals[0]?.kind).toBe("unavailable");
expect(JSON.stringify(input)).not.toMatch(/fileContent|sourceContent|upload/i);
});

it("bounds local branch metadata before scenario validation", () => {
const input = scenarioInputFromLocalBranchMetadata({
scenarioType: "branch_preflight",
login: "miner",
repoFullName: `octo/${"r".repeat(260)}`,
branchName: "b".repeat(260),
baseRef: "m".repeat(260),
linkedIssues: Array.from({ length: 75 }, (_, index) => index + 1),
scenarioNotes: ["n".repeat(SCENARIO_MAX_SIGNAL_DETAIL_CHARS + 100)],
});

expect(input.repo.repoFullName).toHaveLength(SCENARIO_MAX_REPO_FULL_NAME_CHARS);
expect(input.branchState?.branchName).toHaveLength(SCENARIO_MAX_BRANCH_REF_CHARS);
expect(input.branchState?.baseRef).toHaveLength(SCENARIO_MAX_BRANCH_REF_CHARS);
expect(input.issueState?.linkedIssueNumbers).toHaveLength(SCENARIO_MAX_LINKED_ISSUE_NUMBERS);
expect(input.assumptions[0]?.detail).toHaveLength(SCENARIO_MAX_SIGNAL_DETAIL_CHARS);
});
});

describe("invariants", () => {
Expand Down