Files
evanpelle 168db85be5 claude
2026-05-30 19:49:37 -07:00

246 lines
9.1 KiB
TypeScript

export const REPO = { owner: "openfrontio", repo: "OpenFrontIO" } as const;
export const MAINTAINER_LOGIN = "evanpelle";
export const CLAUDE_MODEL = "claude-sonnet-4-6";
export const MAX_CALLS_PER_RUN = 100;
export const RECENT_ISSUES_CONTEXT_SIZE = 50;
export const DIGEST_LOOKBACK_HOURS = 24;
// TODO(maintainer): create a tracking issue, pin it, and set its number here.
// Until set, the digest is logged to stdout instead of posted.
export const DIGEST_ISSUE_NUMBER = 0;
// Toggle to false to let translation issues stay open with `claude-uncertain`
// instead of being auto-closed and redirected to Crowdin.
export const ENABLE_TRANSLATION_CLOSE = true;
export const URLS = {
DEV_DISCORD: "https://discord.gg/K9zernJB5z",
GAME_DISCORD: "https://discord.gg/openfront",
CROWDIN: "https://crowdin.com/project/openfront-mls",
SECURITY_EMAIL: "security@openfront.io",
BILLING_EMAIL: "support@openfront.io",
} as const;
// Labels managed by THIS Action — Claude only applies these.
export const LABELS = {
CLAUDE_TRIAGED: "claude-triaged",
CLAUDE_APPROVED: "claude-approved",
CLAUDE_UNCERTAIN: "claude-uncertain",
BUG: "bug",
QOL_IMPROVEMENT: "qol-improvement",
POSSIBLE_DUPLICATE: "possible-duplicate",
NEEDS_INFO: "needs-info", // shared with Layer A
AUTO_CLOSED_FEATURE: "auto-closed-feature",
AUTO_CLOSED_QUESTION: "auto-closed-question",
AUTO_CLOSED_SUPPORT: "auto-closed-support",
AUTO_CLOSED_BILLING: "auto-closed-billing",
AUTO_CLOSED_TRANSLATION: "auto-closed-translation",
AREA_CLIENT: "area:client",
AREA_CORE: "area:core",
AREA_SERVER: "area:server",
AREA_MAP_GENERATOR: "area:map-generator",
AREA_OTHER: "area:other",
} as const;
// Labels Layer A owns. This Action MUST NOT add or remove these.
// Enforced as a runtime assertion in applyDecision.
export const FORBIDDEN_LABELS: ReadonlySet<string> = new Set([
"approved",
"not-approved",
"stale",
"keep-open",
"auto-closed-stale",
]);
// Layer A label names referenced for digest fetches (read-only).
export const LAYER_A_LABELS = {
STALE: "stale",
NOT_APPROVED: "not-approved",
} as const;
// Note: `security` is intentionally NOT in this list. Closing a GitHub issue
// doesn't make its content private — it stays publicly indexed. Auto-closing
// a security report with a "thanks, please file privately" comment would
// only draw more attention to a still-public exploit description. Instead,
// `security` is routed to `claude-uncertain` so the maintainer can manually
// delete, contact the reporter privately, or process it discreetly.
export const AUTO_CLOSE_CLASSIFICATIONS = [
"feature",
"question",
"support",
"billing",
"translation",
] as const;
export type AutoCloseClassification =
(typeof AUTO_CLOSE_CLASSIFICATIONS)[number];
export const ALL_CLASSIFICATIONS = [
"bug",
"qol-improvement",
"duplicate",
"needs-info",
"feature",
"question",
"support",
"billing",
"security",
"translation",
"uncertain",
] as const;
export type Classification = (typeof ALL_CLASSIFICATIONS)[number];
export const AREA_LABELS = [
LABELS.AREA_CLIENT,
LABELS.AREA_CORE,
LABELS.AREA_SERVER,
LABELS.AREA_MAP_GENERATOR,
LABELS.AREA_OTHER,
] as const;
export type AreaLabel = (typeof AREA_LABELS)[number];
export const LABEL_COLORS: Record<string, string> = {
[LABELS.CLAUDE_TRIAGED]: "EEEEEE",
[LABELS.CLAUDE_APPROVED]: "0E70B0",
[LABELS.CLAUDE_UNCERTAIN]: "5319E7",
[LABELS.BUG]: "D93F0B",
[LABELS.QOL_IMPROVEMENT]: "1D76DB",
[LABELS.POSSIBLE_DUPLICATE]: "FEF2C0",
[LABELS.NEEDS_INFO]: "FBCA04",
[LABELS.AUTO_CLOSED_FEATURE]: "586069",
[LABELS.AUTO_CLOSED_QUESTION]: "586069",
[LABELS.AUTO_CLOSED_SUPPORT]: "586069",
[LABELS.AUTO_CLOSED_BILLING]: "586069",
[LABELS.AUTO_CLOSED_TRANSLATION]: "586069",
[LABELS.AREA_CLIENT]: "C5DEF5",
[LABELS.AREA_CORE]: "C5DEF5",
[LABELS.AREA_SERVER]: "C5DEF5",
[LABELS.AREA_MAP_GENERATOR]: "C5DEF5",
[LABELS.AREA_OTHER]: "C5DEF5",
};
export const LABEL_DESCRIPTIONS: Record<string, string> = {
[LABELS.CLAUDE_TRIAGED]:
"Claude has run triage on this issue (idempotency marker)",
[LABELS.CLAUDE_APPROVED]:
"Claude confidently classified this as actionable bug or QoL — ready for maintainer milestone decision",
[LABELS.CLAUDE_UNCERTAIN]:
"Claude was not confident enough to classify — maintainer needs to triage manually",
[LABELS.BUG]: "Reproducible defect in existing functionality",
[LABELS.QOL_IMPROVEMENT]:
"Small-scope improvement; completable by one contributor in under a week",
[LABELS.POSSIBLE_DUPLICATE]:
"Claude suggested this may duplicate another issue — maintainer confirms",
[LABELS.NEEDS_INFO]:
"Reporter was asked for more info; no special timer — standard stale-close still applies",
[LABELS.AUTO_CLOSED_FEATURE]:
"Closed automatically as a feature request from external contributor",
[LABELS.AUTO_CLOSED_QUESTION]:
"Closed automatically as a developer question — redirected to Dev Discord",
[LABELS.AUTO_CLOSED_SUPPORT]:
"Closed automatically as a player support request — redirected to Game Discord",
[LABELS.AUTO_CLOSED_BILLING]:
"Closed automatically as a billing matter — redirected to support email",
[LABELS.AUTO_CLOSED_TRANSLATION]:
"Closed automatically as a translation matter — redirected to Crowdin",
[LABELS.AREA_CLIENT]:
"Best-guess area: client (rendering, UI, web components)",
[LABELS.AREA_CORE]: "Best-guess area: core simulation",
[LABELS.AREA_SERVER]: "Best-guess area: game server",
[LABELS.AREA_MAP_GENERATOR]: "Best-guess area: map generator",
[LABELS.AREA_OTHER]: "Best-guess area: unclear or outside main components",
};
const AUTOMATION_FOOTER = "— *Automated by Claude triage.*";
export const COMMENTS = {
AUTO_CLOSED_FEATURE: (
author: string,
): string => `Hi @${author}, thanks for the suggestion.
OpenFront doesn't accept feature suggestions as GitHub issues from external contributors. Feature ideas are discussed in our [Dev Discord](${URLS.DEV_DISCORD}) first — if a maintainer thinks the idea is worth pursuing, they'll create the tracking issue.
Please join the Discord and post your idea there.
If you believe this was misclassified — for example, it's actually a bug or a small quality-of-life improvement to existing functionality — please reopen with more detail.
See [CONTRIBUTING.md](https://github.com/${REPO.owner}/${REPO.repo}/blob/main/CONTRIBUTING.md) for the full process.
${AUTOMATION_FOOTER}`,
AUTO_CLOSED_QUESTION: (author: string): string => `Hi @${author},
Development and contributor questions are handled in our [Dev Discord](${URLS.DEV_DISCORD}), not on the issue tracker. You'll get a faster response there.
If you intended to report a bug rather than ask a question, please reopen with repro steps.
${AUTOMATION_FOOTER}`,
AUTO_CLOSED_SUPPORT: (author: string): string => `Hi @${author},
For player support — bugs you're experiencing in-game, connectivity issues, reporting players, account problems — please open a ticket in our [Game Discord](${URLS.GAME_DISCORD}). The community and moderators can help much faster than the issue tracker.
If you've identified a specific reproducible defect (specific steps that always trigger the same broken behavior), please reopen with those details.
${AUTOMATION_FOOTER}`,
AUTO_CLOSED_BILLING: (author: string): string => `Hi @${author},
For anything related to purchases, subscriptions, refunds, premium accounts, or billing — please email ${URLS.BILLING_EMAIL}. We can't process billing issues through GitHub.
${AUTOMATION_FOOTER}`,
AUTO_CLOSED_TRANSLATION: (author: string): string => `Hi @${author},
Translation issues — text errors, requests for new languages, localization corrections — are handled through Crowdin, not the GitHub issue tracker.
Please visit [our Crowdin project](${URLS.CROWDIN}) to suggest fixes or join a translation team.
${AUTOMATION_FOOTER}`,
POSSIBLE_DUPLICATE: (
author: string,
duplicateOf: number,
): string => `Hi @${author},
This issue looks like it might be a duplicate of #${duplicateOf}. A maintainer will confirm.
If you believe it's distinct from #${duplicateOf}, please leave a comment explaining the difference.
— *Automated by Claude triage. This is a suggestion, not a closure.*`,
NEEDS_INFO: (author: string, questions: string[]): string => {
const qList = questions.map((q) => `- ${q}`).join("\n");
return `Hi @${author}, thanks for the report. To help us triage this, could you provide a bit more detail?
${qList}
Without this information, we may not be able to act on this issue.
${AUTOMATION_FOOTER}`;
},
} as const;
export const CLOSE_COMMENT_BY_CLASSIFICATION: Record<
AutoCloseClassification,
(author: string) => string
> = {
feature: COMMENTS.AUTO_CLOSED_FEATURE,
question: COMMENTS.AUTO_CLOSED_QUESTION,
support: COMMENTS.AUTO_CLOSED_SUPPORT,
billing: COMMENTS.AUTO_CLOSED_BILLING,
translation: COMMENTS.AUTO_CLOSED_TRANSLATION,
};
export const CLOSE_LABEL_BY_CLASSIFICATION: Record<
AutoCloseClassification,
string
> = {
feature: LABELS.AUTO_CLOSED_FEATURE,
question: LABELS.AUTO_CLOSED_QUESTION,
support: LABELS.AUTO_CLOSED_SUPPORT,
billing: LABELS.AUTO_CLOSED_BILLING,
translation: LABELS.AUTO_CLOSED_TRANSLATION,
};