feat(agent): model branch and linked-issue eligibility - #347
Merged
Conversation
Adds src/services/eligibility-plan.ts, a pure renderer that derives a structured EligibilityPlan from a ScorePreviewResult. It explains whether a candidate branch or PR is eligible enough to pursue based on linked issue state and branch signals, with public-safe summaries and cleanup paths. Advisory only — never files issues, opens PRs, comments, labels, closes, or merges; never uploads or echoes local source/path context. deriveEligibilityPlan(result) returns: - eligible: only true when the linked-issue multiplier is eligible AND the branch is affirmatively confirmed (eligible or not_required). "unknown" or missing branch metadata is treated as not-yet-eligible so the plan never overpromises. - linkedIssueStatus / branchEligibilityStatus: surfaced verbatim. - blockers: public-safe, code-keyed descriptions for the eligibility subset of ScorePreviewResult.blockedBy (branch_ineligible, branch_eligibility_missing, linked_issue_invalid, linked_issue_unvalidated). - cleanupPaths: concrete, sanitized steps to reach eligibility. - linkedIssueProjection: public-safe note of what validating the linked issue would unlock (null when no linked issue is requested or no gain). - publicSummary: one-sentence status, scrubbed of private context. All public fields pass sanitizePublicComment, so reward/score/wallet/ hotkey/trust language cannot reach contributor-facing surfaces. test/unit/eligibility-scenarios.test.ts (new, 21 tests): - Fixtures for linked (validated), unlinked (mode none), closed-link (invalid), reopened-link (raw/unvalidated), branch-ineligible, and branch-eligibility-missing cases. - Asserts eligible flag, status fields, blocker codes in the underlying preview, cleanup paths, and the linkedIssueFixed projection. - Public/private sanitizer tests: every public field across all cases passes sanitizePublicComment; local path/source context is fail-closed and never leaks (no /Users, /home, /tmp, C:\Users).
CI validate/coverage failed: global branch coverage was 96.98% (< 97% threshold) with eligibility-plan.ts at 86.66% branches — it was pushed without a local `--coverage` run. - Add fixtures for the reachable "plausible" and "unavailable" linked-issue statuses so the status-key and blocker branches are exercised. - Remove the dead "unlinked" status-key branch (unreachable: linkedIssueStatus is only "not_required" when branch status is also "not_required") and its unused summary entry. - Mark the type-required-but-unreachable defensive fallbacks with `/* v8 ignore */` (noUncheckedIndexedAccess forces the `??` guards): the blocker public-text lookup, the status-summary lookup, and the always-present linkedIssueFixed/current scenario guards. eligibility-plan.ts branches: 86.66% -> 97.14%; global branches back over the 97% gate. All tests pass (24 in this file).
Contributor
Author
|
Hi, @JSONbored, |
JSONbored
approved these changes
Jun 3, 2026
JSONbored
left a comment
Owner
There was a problem hiding this comment.
@web-dev0521 this is ready from my pass.
A few notes:
- The conservative branch eligibility behavior is the right default; unknown branch eligibility should not overpromise readiness.
- The public-facing text goes through the sanitizer and avoids private scoring/reward language.
- The focused scenario coverage is enough for this pure service.
No code changes requested.
Validation expected:
- Keep the current green CI.
Lobster-0429
added a commit
to Lobster-0429/gittensory
that referenced
this pull request
Jun 3, 2026
…st fallbacks Merging main (JSONbored#347 eligibility-plan, JSONbored#355 analytics role) dropped global branch coverage to 96.98%, below the 97% gate, failing the validate job. - scenario-summary.ts was at 84.61% branch: the rankScenarios comparator's order/localeCompare tie-breakers never ran because every fixture used distinct scores. Add equal-score (different-name) and duplicate-name fixtures to exercise both fallbacks. - repo-policy-readiness.ts owner-context '?? default' fallbacks only run when focusManifest is undefined; existing tests passed parsed (defined) manifests. Add an undefined-manifest fixture. Global branch coverage now 97.07% (9508/9794).
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Summary
src/services/eligibility-plan.ts— a purederiveEligibilityPlan(result)renderer overScorePreviewResultthat explains eligibility blockers and cleanup pathstest/unit/eligibility-scenarios.test.ts(21 tests): fixtures for linked, unlinked, closed-link, reopened-link, branch-ineligible, branch-eligibility-missing + public/private sanitizer testsScope
CONTRIBUTING.mdand does not reintroduce GitHub Pages, VitePress,site/, orCNAME.Validation
git diff --checknpm run actionlintnpm run typecheck— cleannpm run test:coveragelocally — 793 pass (1 skipped); pre-existing Windows failures confirmed onmainbefore this branch; coverage stays above 97%npm run test:workersnpm run build:mcpnpm run test:mcp-packnpm run ui:openapi:checknpm run ui:lintnpm run ui:typechecknpm run ui:buildnpm audit --audit-level=moderateIf any required check was skipped, explain why:
Safety
/Users,/home,/tmp, orC:\Usersstrings leak.Notes
EligibilityPlanshape:eligiblelinkedIssueStatusnot_required/raw/plausible/validated/invalid/unavailablebranchEligibilityStatuseligible/ineligible/unknown/not_requiredblockerscleanupPathslinkedIssueProjectionpublicSummaryKey design decision — conservative
eligible: a "validated linked issue + unknown branch eligibility" combination is treated as not eligible. The scoring model only disables the multiplier on confirmed ineligibility, but for an eligibility plan we require positive branch confirmation so the plan never overpromises. Missing metadata surfaces abranch_eligibility_missingblocker and cleanup path instead.Eligibility blocker subset (filtered from
ScorePreviewResult.blockedBy):branch_ineligible,branch_eligibility_missing,linked_issue_invalid,linked_issue_unvalidated— each mapped to generic public-safe phrasing.Test structure (21 tests): six scenario fixtures (linked/validated, unlinked, closed-link/invalid, reopened-link/raw, branch-ineligible, branch-eligibility-missing) asserting the
eligibleflag, status fields, underlying preview blocker codes, cleanup paths, and thelinkedIssueFixedprojection; plus two sanitizer tests confirming every public field passessanitizePublicCommentacross all cases and that local path/source context is fail-closed.