Skip to content

feat(scoring): explain issue-discovery validity floor in score breakdown#1984

Closed
bohdansolovie wants to merge 2 commits into
JSONbored:mainfrom
bohdansolovie:feat/score-breakdown-issue-discovery-history
Closed

feat(scoring): explain issue-discovery validity floor in score breakdown#1984
bohdansolovie wants to merge 2 commits into
JSONbored:mainfrom
bohdansolovie:feat/score-breakdown-issue-discovery-history

Conversation

@bohdansolovie

@bohdansolovie bohdansolovie commented Jul 1, 2026

Copy link
Copy Markdown
Contributor

Summary

Adds an issueDiscoveryHistoryMultiplier component to explainScoreBreakdown, matching the merged-PR history breakdown pattern.

The breakdown now mirrors the same upstream invariants as preview.ts:

  • Inactive linked-issue mode (linkedIssueMode: none) — floor not enforced; neutral breakdown even when history fields are supplied.
  • Partial history — only validSolvedIssues or only issueCredibility observed; neutral until both are present.
  • Full history + active linked-issue — full/blocked bands when floors are met or missed.

Why

buildScorePreview already applies the issue-discovery validity multiplier and emits issue_discovery_validity_floor blockers, but the score breakdown never explained that gate. Review on #1979 asked for explicit handling of partial history and inactive linked-issue inputs rather than relying on implicit upstream behavior.

Test plan

  • npx vitest run test/unit/score-breakdown.test.ts
  • Neutral when linked-issue mode is inactive (history supplied)
  • Neutral when only valid-solved or only issue-credibility is observed
  • Neutral when both history fields are unobserved
  • Full when both fields meet upstream floors
  • Blocked with actionable lever when below floors

bohdansolovie and others added 2 commits July 1, 2026 07:01
Surface the upstream MIN_VALID_SOLVED_ISSUES / MIN_ISSUE_CREDIBILITY gate
in explainScoreBreakdown with the same neutral/full/blocked pattern used for
merged-PR history, so linked-issue previews show an actionable lever.

Co-authored-by: Cursor <cursoragent@cursor.com>
Handle inactive linked-issue mode and partial history inputs explicitly
so breakdown wording matches upstream gating in preview.ts.

Co-authored-by: Cursor <cursoragent@cursor.com>
@bohdansolovie bohdansolovie requested a review from JSONbored as a code owner July 1, 2026 05:02
@dosubot dosubot Bot added the size:M This PR changes 30-99 lines, ignoring generated files. label Jul 1, 2026
@gittensory-orb

gittensory-orb Bot commented Jul 1, 2026

Copy link
Copy Markdown

Warning

🟨🟨🟨🟨🟨🟨🟨🟨🟨🟨🟨🟨

⏸️ Gittensory review result - manual review recommended

Review updated: 2026-07-01 06:42:15 UTC

2 files · 1 AI reviewer · no blockers · readiness 48/100 · CI green · clean

⏸️ Suggested Action - Manual Review

  • Touches a guarded path — held for manual review

Review summary
The change adds a dedicated issue-discovery validity-floor component to the breakdown and wires it into the returned component list. The implementation follows the stated invariant: inactive linked-issue mode and partial/unobserved history stay neutral, while fully observed history produces a full or blocked band based on the same floor inputs. The tests cover the important visible branches and also verify public text sanitization through the existing output path.

Nits — 6 non-blocking
  • nit: src/services/score-breakdown.ts:149 duplicates the same neutral object shape across three early returns; extracting a small neutral helper would make future wording-only changes less error-prone.
  • nit: test/unit/score-breakdown.test.ts:126 recreates the same issueDiscoveryRepo and baseInput in several tests; a local helper would reduce fixture drift as this breakdown grows.
  • src/services/score-breakdown.ts:149: consider a small helper such as `neutralIssueDiscoveryHistory(summary, lever)` so all neutral branches keep the same component, band, and leverageScore contract.
  • test/unit/score-breakdown.test.ts:126: factor the repeated repo/base input setup into a helper scoped to this describe block to keep the new branch tests focused on only the changed inputs.
  • Readiness score is below the configured threshold — Use the readiness panel as advisory maintainer context; the score does not block this PR.
  • Touches a guarded path — held for manual review — A maintainer must review and merge this change.
Signal Result Evidence
Code review ✅ No blockers 1 reviewer
Linked issue ⚠️ Missing No linked issue or no-issue rationale found.
Related work ⚠️ 2 scoped overlaps Top overlaps are listed below; lower-confidence bulk is hidden.
Change scope ❌ 8/20 High review scope from cached public metadata (size label size:M; no linked issue context).
Validation posture ❌ 5/25 Preflight is holding this PR; address the blocker before review.
Contributor workload ✅ 10/10 Author activity: 141 registered-repo PR(s), 75 merged, 23 issue(s).
Contributor context ✅ Confirmed Gittensor contributor bohdansolovie; Gittensor profile; 141 PR(s), 23 issue(s).
Gate result ⚠️ Not blocking Advisory; not blocking this PR.
Review context
  • Author: bohdansolovie
  • Role context: outside_contributor
  • Public audience mode: oss maintainer
  • Lane context: Repository registration is not available in the local Gittensory cache.
  • Public profile languages: Python, C++, JavaScript
  • Official Gittensor activity: 141 PR(s), 23 issue(s).
  • Related work: Titles/paths share 6 meaningful terms. (PR #1935)
  • Related work: Titles/paths share 7 meaningful terms. (PR #1978)
Contributor next steps
  • Explain no-issue PR.
  • Review top overlaps.
  • Add a concise scope and risk note.
  • Fix the blocker.
  • Triage stale or unlinked PRs.
  • Refresh registry data or choose a registered active repo.
  • Link the issue being solved, or explicitly explain why this is a no-issue PR.
  • Check active issues and PRs before submitting.
Signal definitions
  • Related work = same linked issue, overlapping active PRs, or title/path similarity.
  • Change scope = cached public metadata such as size labels, draft state, and review-burden hints.
  • Validation posture = whether the PR provides enough public validation/test evidence for maintainer review.
  • Contributor workload = public contributor activity and cleanup pressure, not a repo-wide quality failure.
  • Contributor context = public GitHub/Gittensor identity context; non-Gittensor status is not a blocker.

🟩 Safe / merged · 🟦 Advisory · 🟨 Held for review · 🟥 Blocked / closed


💰 Earn for open-source contributions like this. Gittensor lets GitHub contributors earn for the work they already do — register to start earning →.

Checked by Gittensory, a quiet PR intelligence layer for OSS maintainers.

  • Re-run Gittensory review

@JSONbored JSONbored left a comment

Copy link
Copy Markdown
Owner

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Merge conflicts:

This branch has conflicts that must be resolved
Use the [web editor](https://github.com/JSONbored/gittensory/pull/1984/conflicts) or the command line to resolve conflicts before continuing.

test/unit/score-breakdown.test.ts

Closing.

@JSONbored JSONbored closed this Jul 1, 2026
@github-project-automation github-project-automation Bot moved this from Todo to Done in gittensory - v1 roadmap Jul 1, 2026
JSONbored pushed a commit that referenced this pull request Jul 1, 2026
…eakdown (#2356)

* feat(scoring): surface the saturated base-score value in the score breakdown

explainScoreBreakdown explained every multiplier on scoreEstimate that
scales the score (density, contribution bonus, label, issue, credibility,
review-penalty, review-collateral, open-PR pressure, open-issue spam, the
merged-PR history floor from #1801, the issue-discovery validity floor from
#1984, and the time-decay floor from #1877) but silently omitted the
**baseScore** itself — the saturated-value foundation
`25 × (1 - exp(-src_tok / SRC_TOK_SATURATION_SCALE=58))
 + min(total_token_score / CONTRIBUTION_SCORE_FOR_FULL_BONUS=1500, 1) × MAX_CONTRIBUTION_BONUS=5`
(capped at 30) that flows into estimatedMergedScore before any multiplier
applies. A contributor could see their `densityMultiplier` was healthy but
had no surface for the actual cap contribution or the contribution-bonus
adder.

Add a `baseScoreBreakdown` sibling of `densityBreakdown` (which already
uses `baseTokenGatePassed` as a gate trigger) that:

- returns `blocked` when `baseTokenGatePassed` is false (the change does
  not yet meet the minimum meaningful source-change threshold),
- returns `full` when baseScore is saturated near the 30-point cap and
  names whether the contribution bonus is contributing,
- returns `neutral` when baseScore is mid-curve, with a copy that names
  BOTH the baseScore and the contributionBonus dimensions explicitly
  (avoids the §7 Tier A rule 3 collapse-copy nit — the production copy
  always iterates on observed values for both axes, no "no source
  contribution observed" single-clause ever produced).

Purely additive explanation projection; no scoring behavior change.

Wiring:

- src/services/score-breakdown.ts: add `baseScoreBreakdown`, insert
  at index 0 of the components projection (foundation before density /
  contributionBonus), under the `leverageScore` convention that keeps
  `densityMultiplier` as the canonical top lever when both are blocked
  (baseScore blocked = 70, below densityMultiplier's 75).
- test/unit/score-breakdown.test.ts: include the new `baseScore`
  component in the top-level `arrayContaining` regression list, and add
  a focused test covering the blocked / neutral-with-bonus / saturated
  branches (avoids the §7 Tier A rule 5 missing-both-branches test gap).

Verified locally:

- `git diff --check` clean.
- `npm run typecheck` clean.
- `npm run actionlint` clean.
- `npm run db:migrations:check` — 90 migrations OK, contiguous 0001..0087.
- `npm run build:mcp` clean.
- `npx vitest run test/unit/score-breakdown.test.ts --coverage
  --coverage.include='src/services/score-breakdown.ts'` —
  **15/15 tests pass**; 100% statements; 100% lines; 3 uncovered
  branches in pre-existing code (issueMultiplierBreakdown:240 +
  2 in pre-existing reviewCollateralBreakdown), not touched by this change.

Diff stat: `src/services/score-breakdown.ts` +34 lines,
`test/unit/score-breakdown.test.ts` +60 lines, total +94 lines
(`size:S` territory; orb counts src files primarily per §7 Tier A rule 1).

* fix(scoring): address review nits on base-score breakdown

- Fix test fixture MAX_CONTRIBUTION_BONUS from 25 to 5 (match production)
- Replace hardcoded '30-point cap' with generic 'score cap' text
- Extract BASE_SCORE_SATURATION_DISPLAY_THRESHOLD (29.5) as named constant
- Bump baseScore blocked leverageScore from 70 to 75 (match density gate block)
- Split 3-branch test into separate focused it() blocks
- Add test for hasBonus=false + gate-passed branch

* fix(scoring): derive base-score saturation from preview-estimate cap, not hardcoded threshold

Hardcoding BASE_SCORE_SATURATION_DISPLAY_THRESHOLD=29.5 meant any preview
using different MERGED_PR_BASE_SCORE or MAX_CONTRIBUTION_BONUS constants
would mislabel saturated vs sub-cap.

- Add baseScoreCap to ScorePreviewResult.scoreEstimate (computed alongside
  baseScore in the scoring core, which has the snapshot constants)
- Replace hardcoded 29.5 with BASE_SCORE_SATURATION_RATIO=0.95 applied to
  baseScore / baseScoreCap — always relative to the active model's cap
- baseScoreCap is undefined when fixedBaseScore is in effect
- Add regression test with MERGED_PR_BASE_SCORE=50, MAX_CONTRIBUTION_BONUS=10
  proving the threshold adapts to non-default constants

* fix: guard baseScoreCap > 0 before division, fix test comment

- Add baseScoreCap > 0 guard before saturation ratio division
- Fix test comment: 993 is the 95% threshold value, not 'full bonus'

* fix: remove dead locals from test, handle baseScoreCap undefined copy

* test: cover fixedBaseScore and saturation model cap branches

- fixedBaseScore override → baseScoreCap undefined, copy says 'fixed base score override'
- pending_saturation_model → cap = MERGED_PR_BASE_SCORE + MAX_CONTRIBUTION_BONUS
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

gittensor:feature Gittensor-scored feature linked to a feature issue — scores a 1.25x multiplier. gittensor Gittensor contributor context size:M This PR changes 30-99 lines, ignoring generated files.

Projects

No open projects
Status: Done

Development

Successfully merging this pull request may close these issues.

2 participants