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
148 changes: 140 additions & 8 deletions apps/gittensory-ui/public/openapi.json
Original file line number Diff line number Diff line change
Expand Up @@ -3456,6 +3456,63 @@
"credibilityObserved"
]
},
"branchEligibility": {
"type": "object",
"properties": {
"required": {
"type": "boolean"
},
"status": {
"type": "string",
"enum": [
"eligible",
"ineligible",
"unknown",
"not_required"
]
},
"evidence": {
"type": "string",
"enum": [
"provided",
"missing"
]
},
"source": {
"type": "string",
"enum": [
"github_metadata",
"local_metadata",
"registry",
"user_supplied",
"missing"
]
},
"reason": {
"type": "string"
},
"checkedAt": {
"type": "string"
},
"stale": {
"type": "boolean"
},
"warnings": {
"type": "array",
"items": {
"type": "string"
}
}
},
"required": [
"required",
"status",
"evidence",
"source",
"stale",
"warnings"
]
},
"effectiveEstimatedScore": {
"type": "number"
},
Expand All @@ -3478,7 +3535,9 @@
"review_penalty",
"metadata_only",
"linked_issue_invalid",
"linked_issue_unvalidated"
"linked_issue_unvalidated",
"branch_ineligible",
"branch_eligibility_missing"
]
},
"severity": {
Expand Down Expand Up @@ -3663,7 +3722,9 @@
"review_penalty",
"metadata_only",
"linked_issue_invalid",
"linked_issue_unvalidated"
"linked_issue_unvalidated",
"branch_ineligible",
"branch_eligibility_missing"
]
},
"severity": {
Expand Down Expand Up @@ -3836,6 +3897,7 @@
"scoreEstimate",
"linkedIssueMultiplier",
"gates",
"branchEligibility",
"effectiveEstimatedScore",
"underlyingPotentialScore",
"blockedBy",
Expand Down Expand Up @@ -4591,7 +4653,9 @@
"review_penalty",
"metadata_only",
"linked_issue_invalid",
"linked_issue_unvalidated"
"linked_issue_unvalidated",
"branch_ineligible",
"branch_eligibility_missing"
]
},
"severity": {
Expand Down Expand Up @@ -4837,7 +4901,9 @@
"review_penalty",
"metadata_only",
"linked_issue_invalid",
"linked_issue_unvalidated"
"linked_issue_unvalidated",
"branch_ineligible",
"branch_eligibility_missing"
]
},
"severity": {
Expand Down Expand Up @@ -5083,7 +5149,9 @@
"review_penalty",
"metadata_only",
"linked_issue_invalid",
"linked_issue_unvalidated"
"linked_issue_unvalidated",
"branch_ineligible",
"branch_eligibility_missing"
]
},
"severity": {
Expand Down Expand Up @@ -5329,7 +5397,9 @@
"review_penalty",
"metadata_only",
"linked_issue_invalid",
"linked_issue_unvalidated"
"linked_issue_unvalidated",
"branch_ineligible",
"branch_eligibility_missing"
]
},
"severity": {
Expand Down Expand Up @@ -5575,7 +5645,9 @@
"review_penalty",
"metadata_only",
"linked_issue_invalid",
"linked_issue_unvalidated"
"linked_issue_unvalidated",
"branch_ineligible",
"branch_eligibility_missing"
]
},
"severity": {
Expand Down Expand Up @@ -5738,7 +5810,9 @@
"review_penalty",
"metadata_only",
"linked_issue_invalid",
"linked_issue_unvalidated"
"linked_issue_unvalidated",
"branch_ineligible",
"branch_eligibility_missing"
]
},
"severity": {
Expand Down Expand Up @@ -5854,6 +5928,63 @@
"notes"
]
},
"branchEligibility": {
"type": "object",
"properties": {
"required": {
"type": "boolean"
},
"status": {
"type": "string",
"enum": [
"eligible",
"ineligible",
"unknown",
"not_required"
]
},
"evidence": {
"type": "string",
"enum": [
"provided",
"missing"
]
},
"source": {
"type": "string",
"enum": [
"github_metadata",
"local_metadata",
"registry",
"user_supplied",
"missing"
]
},
"reason": {
"type": "string"
},
"checkedAt": {
"type": "string"
},
"stale": {
"type": "boolean"
},
"warnings": {
"type": "array",
"items": {
"type": "string"
}
}
},
"required": [
"required",
"status",
"evidence",
"source",
"stale",
"warnings"
]
},
"rewardRisk": {
"$ref": "#/components/schemas/RepoRewardRisk"
},
Expand Down Expand Up @@ -6185,6 +6316,7 @@
"scenarioScorePreview",
"observedPullRequestScenarios",
"githubBranchStatus",
"branchEligibility",
"rewardRisk",
"scoreBlockers",
"branchQualityBlockers",
Expand Down
41 changes: 39 additions & 2 deletions packages/gittensory-mcp/bin/gittensory-mcp.js
Original file line number Diff line number Diff line change
Expand Up @@ -66,6 +66,14 @@ const localDiffShape = {
commitMessage: z.string().optional(),
};

const branchEligibilityShape = {
status: z.enum(["eligible", "ineligible", "unknown"]),
source: z.enum(["github_metadata", "local_metadata", "registry", "user_supplied"]).optional(),
reason: z.string().optional(),
checkedAt: z.string().optional(),
stale: z.boolean().optional(),
};

const localScoreShape = {
...localDiffShape,
targetKey: z.string().optional(),
Expand All @@ -82,6 +90,7 @@ const localScoreShape = {
expectedOpenPrCountAfterMerge: z.number().int().min(0).optional(),
projectedCredibility: z.number().min(0).max(1).optional(),
scenarioNotes: z.array(z.string()).optional(),
branchEligibility: z.object(branchEligibilityShape).strict().optional(),
scorePreviewCommand: z.string().optional(),
};

Expand All @@ -106,6 +115,7 @@ const currentBranchShape = {
expectedOpenPrCountAfterMerge: z.number().int().min(0).optional(),
projectedCredibility: z.number().min(0).max(1).optional(),
scenarioNotes: z.array(z.string()).optional(),
branchEligibility: z.object(branchEligibilityShape).strict().optional(),
validation: z
.array(
z.object({
Expand Down Expand Up @@ -488,6 +498,7 @@ async function runCli(args) {
expectedOpenPrCountAfterMerge: optionalInteger(options.expectedOpenPrs),
projectedCredibility: optionalNumber(options.projectedCredibility),
scenarioNotes: options.scenarioNote,
branchEligibility: branchEligibilityFromOptions(options),
validation: validationFromOptions(options),
scorePreviewCommand: options.scorePreviewCommand,
});
Expand Down Expand Up @@ -543,6 +554,7 @@ async function runAgentCli(args) {
expectedOpenPrCountAfterMerge: optionalInteger(options.expectedOpenPrs),
projectedCredibility: optionalNumber(options.projectedCredibility),
scenarioNotes: options.scenarioNote,
branchEligibility: branchEligibilityFromOptions(options),
validation: validationFromOptions(options),
scorePreviewCommand: options.scorePreviewCommand,
});
Expand Down Expand Up @@ -644,8 +656,8 @@ function printHelp() {
gittensory-mcp changelog [--json]
gittensory-mcp doctor [--cwd path] [--json]
gittensory-mcp init-client --print codex|claude|cursor|mcp [--json]
gittensory-mcp analyze-branch --login <github-login> [--repo owner/repo] [--base origin/main] [--pending-merged-prs 3] [--expected-open-prs 0] [--projected-credibility 0.8] [--scenario-note "..."] [--validation "passed|npm test|summary"] [--json]
gittensory-mcp preflight --login <github-login> [--repo owner/repo] [--base origin/main] [--pending-merged-prs 3] [--expected-open-prs 0] [--projected-credibility 0.8] [--validation "passed|npm test|summary"] [--json]
gittensory-mcp analyze-branch --login <github-login> [--repo owner/repo] [--base origin/main] [--branch-eligibility eligible|ineligible|unknown] [--pending-merged-prs 3] [--expected-open-prs 0] [--projected-credibility 0.8] [--scenario-note "..."] [--validation "passed|npm test|summary"] [--json]
gittensory-mcp preflight --login <github-login> [--repo owner/repo] [--base origin/main] [--branch-eligibility eligible|ineligible|unknown] [--pending-merged-prs 3] [--expected-open-prs 0] [--projected-credibility 0.8] [--validation "passed|npm test|summary"] [--json]
gittensory-mcp agent plan --login <github-login> [--repo owner/repo] [--json]
gittensory-mcp agent status <run-id> [--json]
gittensory-mcp agent explain <run-id> [--json]
Expand Down Expand Up @@ -1429,6 +1441,7 @@ async function previewLocalScore(input) {
expectedOpenPrCountAfterMerge: input.expectedOpenPrCountAfterMerge,
projectedCredibility: input.projectedCredibility,
scenarioNotes: input.scenarioNotes,
branchEligibility: input.branchEligibility,
metadataOnly: !upstreamPreview.ok,
};
return {
Expand All @@ -1447,6 +1460,30 @@ async function previewLocalScore(input) {
};
}

function branchEligibilityFromOptions(options) {
const status = options.branchEligibility ?? options.branchEligibilityStatus;
if (!["eligible", "ineligible", "unknown"].includes(status)) return undefined;
const source = ["github_metadata", "local_metadata", "registry", "user_supplied"].includes(options.branchEligibilitySource) ? options.branchEligibilitySource : "user_supplied";
return stripUndefined({
status,
source,
reason: options.branchEligibilityReason,
checkedAt: options.branchEligibilityCheckedAt,
stale: optionalBoolean(options.branchEligibilityStale),
});
}

function optionalBoolean(value) {
if (value === undefined) return undefined;
if (value === true) return true;
if (typeof value === "string") {
const normalized = value.trim().toLowerCase();
if (["false", "0", "no", "off"].includes(normalized)) return false;
if (["true", "1", "yes", "on"].includes(normalized)) return true;
}
return Boolean(value);
}

function toolResult(summary, data) {
return {
content: [
Expand Down
1 change: 1 addition & 0 deletions packages/gittensory-mcp/lib/local-branch.js
Original file line number Diff line number Diff line change
Expand Up @@ -76,6 +76,7 @@ export function collectLocalBranchMetadata(input) {
scenarioNotes: input.scenarioNotes,
pendingCommitCount,
ciStatusHints,
branchEligibility: input.branchEligibility,
};
return stripUndefined(payload);
}
Expand Down
12 changes: 12 additions & 0 deletions src/api/routes.ts
Original file line number Diff line number Diff line change
Expand Up @@ -278,6 +278,16 @@ const linkedIssueContextSchema = z
})
.strict();

const branchEligibilitySchema = z
.object({
status: z.enum(["eligible", "ineligible", "unknown"]),
source: z.enum(["github_metadata", "local_metadata", "registry", "user_supplied"]).optional(),
reason: z.string().max(MAX_LOCAL_BRANCH_TEXT_CHARS).optional(),
checkedAt: z.string().max(MAX_LOCAL_BRANCH_REF_CHARS).optional(),
stale: z.boolean().optional(),
})
.strict();

const localBranchAnalysisSchema = z
.object({
login: z.string().min(1).max(MAX_LOCAL_BRANCH_REF_CHARS),
Expand Down Expand Up @@ -306,6 +316,7 @@ const localBranchAnalysisSchema = z
pendingCommitCount: z.number().int().min(0).optional(),
ciStatusHints: z.array(z.string().max(MAX_LOCAL_BRANCH_TEXT_CHARS)).max(20).optional(),
focusManifest: z.record(z.unknown()).optional(),
branchEligibility: branchEligibilitySchema.optional(),
})
.strict();

Expand Down Expand Up @@ -334,6 +345,7 @@ const scorePreviewSchema = z.object({
expectedOpenPrCountAfterMerge: z.number().int().min(0).optional(),
projectedCredibility: z.number().min(0).max(1).optional(),
scenarioNotes: z.array(z.string()).max(20).optional(),
branchEligibility: branchEligibilitySchema.optional(),
});

const agentSurfaceSchema = z.enum(["api", "mcp", "github_comment"]).default("api");
Expand Down
Loading