Skip to content

feat(analytics): classify rejected recommendation outcomes#330

Open
bittoby wants to merge 1 commit into
JSONbored:mainfrom
bittoby:feat/302-recommendation-outcome-classification
Open

feat(analytics): classify rejected recommendation outcomes#330
bittoby wants to merge 1 commit into
JSONbored:mainfrom
bittoby:feat/302-recommendation-outcome-classification

Conversation

@bittoby
Copy link
Copy Markdown
Contributor

@bittoby bittoby commented Jun 2, 2026

Summary

Closes #302

  • Adds deterministic recommendation outcome classification for explicit feedback, including rejected.
  • Separates persisted recommendation outcomes by explicit versus inferred source.
  • Keeps the change private/internal without changing the public OpenAPI/UI contract.

Scope

  • This PR is focused and does not mix unrelated backend, UI, MCP, docs, dependency, and deploy changes.
  • This follows CONTRIBUTING.md and does not reintroduce GitHub Pages, VitePress, site/, or CNAME.
  • I linked an issue, or this is small enough that the summary explains why an issue is not needed.

Validation

  • git diff --check
  • npm run actionlint
  • npm run typecheck
  • npm run test:coverage locally; global coverage stays at or above 97% for lines, statements, functions, and branches (aim for 98%+ branch coverage locally so CI variance does not fail near the threshold)
  • npm run test:workers
  • npm run build:mcp
  • npm run test:mcp-pack
  • npm run ui:openapi:check
  • npm run ui:lint
  • npm run ui:typecheck
  • npm run ui:build
  • npm audit --audit-level=moderate
  • New or changed behavior has unit/integration tests for new branches, fallback paths, and sanitizer boundaries

If any required check was skipped, explain why:

  • Skipped npm run actionlint because this PR does not change GitHub Actions workflows.
  • Skipped npm run test:workers, npm run build:mcp, and npm run test:mcp-pack because this PR does not change worker packaging or MCP behavior.
  • Skipped npm audit --audit-level=moderate; no dependency changes were made.
  • npm run test:coverage passed with 64 files passed, 1 skipped, 835 tests passed, 1 skipped, and branch coverage at 97.12%.

Safety

  • No secrets, wallet details, hotkeys, coldkeys, user PATs, private keys, raw trust scores, private rankings, or private maintainer evidence are exposed.
  • Public GitHub text stays sanitized, low-noise, and does not imply compensation guarantees or optimization tactics.
  • Auth, cookie, CORS, GitHub App, Cloudflare, or session changes include negative-path tests.
  • API/OpenAPI/MCP behavior is updated and tested where needed.
  • UI changes use live API data or real empty/error/loading states, not production mock/demo fallbacks.
  • Visible UI changes include screenshots or a short recording.
  • Public docs/changelogs are updated where needed; changelogs are only edited for release-prep PRs.

Notes

  • Adds rejected as a persisted recommendation outcome state.
  • Adds source metadata for explicit versus inferred recommendation outcomes.
  • Records explicit accepted/rejected outcomes from command feedback when the answer can be traced to an agent run/action.
  • Leaves historical command answers without run/action metadata as command usefulness feedback only.
  • Prevents later inferred lifecycle outcomes from overwriting explicit feedback outcomes.
  • Does not change openapi.json or the public UI/OpenAPI contract.

@bittoby bittoby requested a review from JSONbored as a code owner June 2, 2026 23:59
@dosubot dosubot Bot added the size:L This PR changes 100-499 lines, ignoring generated files. label Jun 2, 2026
@github-actions github-actions Bot added the feature New feature or request label Jun 2, 2026
@dosubot
Copy link
Copy Markdown

dosubot Bot commented Jun 3, 2026

Related Knowledge

1 document with suggested updates is ready for review.

gittensory

Architecture
View Suggested Changes
@@ -301,7 +301,7 @@
 - **agentRuns** — Agent orchestration runs with objective and status ([src/db/schema.ts](https://github.com/JSONbored/gittensory/blob/f63f125e2126dc58ac5a63e88f4f33d1e13b2979/src/db/schema.ts#L408-L428))
 - **agentActions** — Agent action recommendations with risk impact ([src/db/schema.ts](https://github.com/JSONbored/gittensory/blob/f63f125e2126dc58ac5a63e88f4f33d1e13b2979/src/db/schema.ts#L430-L457))
 - **agentContextSnapshots** — Agent context with decision pack and signal snapshot IDs ([src/db/schema.ts](https://github.com/JSONbored/gittensory/blob/f63f125e2126dc58ac5a63e88f4f33d1e13b2979/src/db/schema.ts#L459-L474))
-- **agentRecommendationOutcomes** — Tracks deterministic recommendation outcome classifications (merged, closed, improved, accepted, ignored, stale) for agent actions, including target details (repo/PR/issue), confidence levels, and maintainer-lane flag. Indexed by action ID, actor/state, target, and maintainer-lane.
+- **agentRecommendationOutcomes** — Tracks deterministic recommendation outcome classifications (merged, closed, improved, accepted, rejected, ignored, stale) for agent actions, including target details (repo/PR/issue), confidence levels, source metadata (explicit or inferred), and maintainer-lane flag. Explicit outcomes (from direct user feedback) cannot be overwritten by later inferred outcomes. Indexed by action ID, actor/state, target, maintainer-lane, and (actor_login, source, updated_at).
 - **githubAgentCommandAnswers** — Stores command invocations and responses from the GitHub agent (@gittensory), including repo, issue number, command name, request/response comment IDs, actor kind, and metadata. Indexed by (repo_full_name, issue_number) and (command, updated_at).
 - **githubAgentCommandFeedback** — Records usefulness votes (thumbs-up/thumbs-down reactions) on command answers, with unique constraint on (answer_id, actor_hash) to deduplicate feedback per actor. Tracks vote, source (reaction or app feedback), actor kind, and links to github_agent_command_answers via foreign key. Indexed by (answer_id, actor_hash), (command, updated_at), and (repo_full_name, issue_number).
 - **syncRuns** — Sync job execution tracking ([src/db/schema.ts](https://github.com/JSONbored/gittensory/blob/f63f125e2126dc58ac5a63e88f4f33d1e13b2979/src/db/schema.ts#L523-L533))
@@ -492,18 +492,21 @@
 
 #### Recommendation Outcomes (`src/services/recommendation-outcomes.ts`)
 
-The recommendation outcome service evaluates the fate of agent action recommendations by matching them against later PR and issue activity. The `evaluateRecommendationOutcomes()` function fetches recent agent runs and contributor PR/issue history, then deterministically classifies each action into one of six outcome states:
+The recommendation outcome service evaluates the fate of agent action recommendations by matching them against later PR and issue activity. The `evaluateRecommendationOutcomes()` function fetches recent agent runs and contributor PR/issue history, then deterministically classifies each action into one of seven outcome states:
 
 - **merged** — The recommended PR was merged after the recommendation.
 - **closed** — The recommended PR was closed without merging, or the recommended issue was closed.
 - **improved** — The recommended PR remains open but now has approval or clean mergeability evidence.
 - **accepted** — Later cached activity matches the recommendation target (PR/issue created or updated).
+- **rejected** — Recommendation was explicitly rejected or dismissed through direct user feedback.
 - **stale** — No activity past the stale-outcome window (default 14 days).
 - **ignored** — No matching activity after the ignored-outcome window (default 7 days), but not yet stale.
 
+Each outcome record includes a `source` field that distinguishes between **explicit** and **inferred** outcomes. Explicit outcomes are derived from direct user feedback (e.g., thumbs-down reactions or explicit dismissal), while inferred outcomes are derived from PR/issue lifecycle events. Explicit outcomes are protected from later inferred lifecycle overwrite—once an outcome is marked as explicit, it cannot be replaced by an inferred classification.
+
 The classification logic in `classifyRecommendationOutcome()` uses deterministic matching rules: exact target PR/issue match, linked issue match, or later repo activity. The `isMaintainerLane()` function identifies maintainer-lane outcomes by checking if the actor is the repo owner, or if the PR/issue has a maintainer-associated author association (OWNER, MEMBER, COLLABORATOR). Maintainer-lane outcomes are tracked but kept separate from public contributor feedback.
 
-The service persists outcome records to the `agentRecommendationOutcomes` table with confidence levels (high for exact matches, medium for partial matches, low for no-match). Aggregate feedback is consumed by the decision pack system to adjust priority scores and provide private feedback signals for contributor decisions and repo decision confidence/risk indicators.
+The service persists outcome records to the `agentRecommendationOutcomes` table with confidence levels (high for exact matches, medium for partial matches, low for no-match). Aggregate feedback is consumed by the decision pack system to adjust priority scores and provide private feedback signals for contributor decisions and repo decision confidence/risk indicators. Summary statistics track counts of explicit vs inferred outcomes in addition to state-based counts.
 
 ## Testing
 

✅ Accepted

How did I do? Any feedback?  Join Discord

@bittoby bittoby force-pushed the feat/302-recommendation-outcome-classification branch 4 times, most recently from 2f2ada8 to 545daa9 Compare June 3, 2026 00:36
Copy link
Copy Markdown
Owner

@JSONbored JSONbored left a comment

Choose a reason for hiding this comment

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

@bittoby thanks, this is the right area for #302, but one behavior gap needs fixing.

A few notes:

  • Explicit accepted vs rejected feedback is a good addition.
  • The tests cover the explicit feedback path, but the summary output still needs to expose rejected outcomes consistently.

Required changes:

  • Retitle the PR to a scoped Conventional Commit title, for example feat(analytics): classify rejected recommendation outcomes.
  • Include rejected in the outcome state bucket output, not just the totals.
  • Check migration numbering against the other open analytics PRs before merge so we do not land colliding migration filenames.

Validation expected:

  • Add or update a focused test proving rejected outcomes appear in the state bucket summary.
  • Keep npm run test:ci green.

@JSONbored JSONbored changed the title feat: add explicit rejected recommendation outcome classification feat(api): add explicit rejected recommendation outcome classification Jun 3, 2026
@bittoby bittoby force-pushed the feat/302-recommendation-outcome-classification branch from 545daa9 to bd647c1 Compare June 3, 2026 10:54
@bittoby bittoby changed the title feat(api): add explicit rejected recommendation outcome classification feat(analytics): classify rejected recommendation outcomes Jun 3, 2026
@bittoby bittoby force-pushed the feat/302-recommendation-outcome-classification branch from bd647c1 to 3509a6f Compare June 3, 2026 11:35
@bittoby
Copy link
Copy Markdown
Contributor Author

bittoby commented Jun 3, 2026

@JSONbored I've updated. pls review again.

@JSONbored JSONbored self-requested a review June 3, 2026 22:11
Copy link
Copy Markdown
Owner

@JSONbored JSONbored left a comment

Choose a reason for hiding this comment

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

@bittoby this needs a migration sequencing cleanup.

A few notes:

  • Explicit accepted/rejected recommendation outcomes are useful and the inferred-vs-explicit guard is the right behavior.
  • The branch is clean at merge-tree level, but migration numbering is stale.
  • Current main already has 0018_recommendation_outcome_surface_snapshot.sql and 0019_product_usage_event_role.sql.

Required changes:

  • Rebase onto current main.
  • Renumber migrations/0018_agent_recommendation_outcome_source.sql to the next available migration.
  • Re-run schema and recommendation-outcome tests after renumbering.

Validation expected:

  • git diff --check
  • npm run typecheck
  • npm run test:coverage

@bittoby bittoby force-pushed the feat/302-recommendation-outcome-classification branch from 50a3afc to b707bdc Compare June 4, 2026 12:23
@bittoby
Copy link
Copy Markdown
Contributor Author

bittoby commented Jun 4, 2026

@JSONbored I've addressed your feedback all. pls review again.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

feature New feature or request size:L This PR changes 100-499 lines, ignoring generated files.

Projects

Status: Todo

Development

Successfully merging this pull request may close these issues.

feat(analytics): classify recommendation outcomes

2 participants