feat(agent): add recommendation outcome feedback - #229
Conversation
|
Related Knowledge 2 documents with suggested updates are ready for review. gittensory ArchitectureView Suggested Changes@@ -301,6 +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.
- **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))
@@ -355,7 +356,7 @@
## Key Backend Modules
-The backend intelligence of Gittensory is organized into nine major modules, each handling a distinct domain of the system's functionality. These modules operate as the core orchestration layer between GitHub data, Gittensor registry state, and Gittensory's signals intelligence.
+The backend intelligence of Gittensory is organized into major modules, each handling a distinct domain of the system's functionality. These modules operate as the core orchestration layer between GitHub data, Gittensor registry state, and Gittensory's signals intelligence.
### Scoring Module (`src/scoring/`)
@@ -480,6 +481,29 @@
- **Extension-scoped tokens** for the browser extension, issued via `POST /v1/auth/extension/session`.
Rate limiting is enforced via a Durable Object named `RateLimiter` ([wrangler.jsonc](https://github.com/JSONbored/gittensory/blob/f63f125e2126dc58ac5a63e88f4f33d1e13b2979/wrangler.jsonc#L58-L65)), which tracks request counts per actor and applies sliding window rate limits.
+
+### Services Layer (`src/services/`)
+
+The services layer provides cross-cutting functionality that spans multiple backend domains. Key service modules include:
+
+#### Decision Packs (`src/services/decision-pack.ts`)
+
+The decision pack service builds comprehensive contributor action plans by integrating data from the registry, signals engine, scoring model, GitHub activity, and recommendation outcome feedback. The `buildAndPersistContributorDecisionPack()` function orchestrates the full pipeline: evaluating recommendation outcomes, fetching repository context, building role analysis, and ranking opportunities. Decision packs include a `recommendationOutcomeFeedback` field containing aggregate statistics (positive/negative counts by state: merged, closed, improved, accepted, ignored, stale), per-repository summaries with signal classification (positive, negative, mixed, neutral), and priority score adjustments based on feedback (+8 for positive signal, -12 for negative, -4 for mixed). Maintainer-lane outcomes are separated and tracked independently to preserve privacy boundaries.
+
+#### 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:
+
+- **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).
+- **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.
+
+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.
## Testing
✅ Accepted Gittensory ArchitectureView Suggested Changes@@ -64,6 +64,8 @@
The decision pack service aggregates multi-source data into per-contributor decision artifacts [[18]](https://github.com/JSONbored/gittensory/blob/40c019c82d87568c232fbe7fabf50212e983075a/src/services/decision-pack.ts#L1-L34). It enforces a 6-hour freshness threshold (`DECISION_PACK_MAX_AGE_MS`) and a 15-second debounce window (`DECISION_PACK_REBUILD_DEBOUNCE_MS`) to prevent excessive rebuild storms [[19]](https://github.com/JSONbored/gittensory/blob/40c019c82d87568c232fbe7fabf50212e983075a/src/services/decision-pack.ts#L54-L55).
Decision packs classify recommendations into five states: pursue, cleanup_first, maintainer_lane, avoid_for_now, and watch. Action kinds include PR cleanup/landing, new PR filing, issue discovery, and maintainer-specific work. Freshness states track whether a pack is fresh, stale, rebuilding, or missing.
+
+`buildAndPersistContributorDecisionPack()` now calls `evaluateRecommendationOutcomes()` before building the pack to refresh outcome data, then fetches `AgentRecommendationOutcomeSummary` via `getAgentRecommendationOutcomeSummary()`. The resulting `ContributorDecisionPack` includes `recommendationOutcomeFeedback` with aggregate stats (positive/negative counts, states breakdown). Each `RepoDecision` receives per-repo feedback with priority score adjustments: +8 for positive signal, -12 for negative, -4 for mixed. Feedback is incorporated into risk reasons and "why this helps" explanations. Maintainer-lane outcomes are kept separate to preserve privacy boundaries.
### **E. Agent Orchestrator (`src/services/agent-orchestrator.ts`)**
@@ -103,7 +105,9 @@
### **I. Database Layer (`src/db/`)**
-The database uses Drizzle ORM with Cloudflare D1 (SQLite) [[30]](https://github.com/JSONbored/gittensory/blob/40c019c82d87568c232fbe7fabf50212e983075a/src/db/client.ts). The schema spans 50+ tables across 16 migrations [[31]](https://github.com/JSONbored/gittensory/blob/40c019c82d87568c232fbe7fabf50212e983075a/migrations), grouped into six categories: GitHub integration (installations, repositories, repositorySettings), sync state (repoSyncState, repoSyncSegments), scoring (scoringModelSnapshots, scorePreviews, contributorEvidence), agent orchestration (agentRuns, agentActions, agentContextSnapshots), auth/compliance (authSessions, auditEvents), and analytics (productUsageEvents, productUsageDailyRollups) [[32]](https://github.com/JSONbored/gittensory/blob/40c019c82d87568c232fbe7fabf50212e983075a/src/db/schema.ts).
+The database uses Drizzle ORM with Cloudflare D1 (SQLite) [[30]](https://github.com/JSONbored/gittensory/blob/40c019c82d87568c232fbe7fabf50212e983075a/src/db/client.ts). The schema spans 50+ tables across 17 migrations [[31]](https://github.com/JSONbored/gittensory/blob/40c019c82d87568c232fbe7fabf50212e983075a/migrations), grouped into six categories: GitHub integration (installations, repositories, repositorySettings), sync state (repoSyncState, repoSyncSegments), scoring (scoringModelSnapshots, scorePreviews, contributorEvidence), agent orchestration (agentRuns, agentActions, agentContextSnapshots, agentRecommendationOutcomes), auth/compliance (authSessions, auditEvents), and analytics (productUsageEvents, productUsageDailyRollups) [[32]](https://github.com/JSONbored/gittensory/blob/40c019c82d87568c232fbe7fabf50212e983075a/src/db/schema.ts).
+
+The `agentRecommendationOutcomes` table (migration 0017) stores deterministic outcome classifications for agent actions. Fields include action and run identifiers (with foreign keys to `agentActions` and `agentRuns`), target identifiers (repo, PR number, issue number), outcome state (merged, closed, improved, accepted, ignored, stale), outcome target details (type, repo, PR/issue number), maintainer lane flag to separate maintainer vs contributor activity, confidence level (high, medium, low) with reason text, detection timestamp and source update timestamp for time-based analysis, and metadata JSON for additional context. Indexes optimize queries by actor+state, target lookups, and maintainer-lane filtering.
### **J. Queue Processor (`src/queue/`)**
@@ -174,7 +178,7 @@
packages/gittensory-mcp/ # npm MCP package (@jsonbored/gittensory-mcp)
apps/gittensory-ui/ # web frontend (TypeScript/Vite)
apps/gittensory-extension/# browser extension (Manifest V3)
-migrations/ # 16 Drizzle/D1 SQL migrations
+migrations/ # 17 Drizzle/D1 SQL migrations
scripts/ # utility and build scripts
test/ # test files
```
@@ -211,6 +215,8 @@
- `auth/` — github-oauth, rate-limit, security
- `utils/` — crypto helpers, JSON utilities
+`services/` includes `recommendation-outcomes.ts`, which implements `evaluateRecommendationOutcomes()` to process agent runs and classify recommendation outcomes. It matches agent actions against cached PR/issue activity to determine what happened after a recommendation, using heuristics: exact PR/issue match, linked issue to PR, later repo activity from the same contributor. Time windows (default 14 days for stale, 7 days for ignored) classify unmatched recommendations. The module generates summaries aggregated by actor and by repository.
+
### MCP Package Structure
The `@jsonbored/gittensory-mcp` package (v0.3.0) ships as a standalone npm module with Node >= 22 requirement [[55]](https://github.com/JSONbored/gittensory/blob/40c019c82d87568c232fbe7fabf50212e983075a/packages/gittensory-mcp/package.json#L1-L48):
@@ -336,7 +342,7 @@
### Data Layer
-**Database:** Cloudflare D1 (SQLite at the edge) with a binding named [`DB`](https://github.com/JSONbored/gittensory/blob/40c019c82d87568c232fbe7fabf50212e983075a/wrangler.jsonc#L50-L57) pointing to the `gittensory` database. The schema comprises [50+ tables across 16 migrations](https://github.com/JSONbored/gittensory/blob/40c019c82d87568c232fbe7fabf50212e983075a/migrations), spanning GitHub integration, sync state, scoring, agent orchestration, auth/compliance, and analytics.
+**Database:** Cloudflare D1 (SQLite at the edge) with a binding named [`DB`](https://github.com/JSONbored/gittensory/blob/40c019c82d87568c232fbe7fabf50212e983075a/wrangler.jsonc#L50-L57) pointing to the `gittensory` database. The schema comprises [50+ tables across 17 migrations](https://github.com/JSONbored/gittensory/blob/40c019c82d87568c232fbe7fabf50212e983075a/migrations), spanning GitHub integration, sync state, scoring, agent orchestration, auth/compliance, and analytics.
**ORM:** [Drizzle ORM ^0.45.0](https://github.com/JSONbored/gittensory/blob/40c019c82d87568c232fbe7fabf50212e983075a/package.json#L62) provides type-safe query construction and schema management.
✅ Accepted |
There was a problem hiding this comment.
@MkDev11 the recommendation outcome feedback model is useful, especially keeping maintainer-lane outcomes separate from contributor feedback.
A few notes:
- CI is green on the branch.
- Current
mainnow conflicts with this PR inapps/gittensory-ui/public/openapi.json,src/openapi/schemas.ts, andsrc/services/decision-pack.ts. - The PR checks the screenshot box but does not provide actual screenshot or recording evidence.
Required changes:
- Resolve the merge conflicts against current
main. - Regenerate OpenAPI artifacts after resolving backend schema changes.
- Attach screenshot/recording evidence for the frontend-touched artifact path, or remove the frontend touch if it is not part of the change.
Validation expected:
- Re-run
npm run test:ci. - Confirm the migration, OpenAPI schema, and decision-pack tests still pass together.
…-outcome-feedback # Conflicts: # apps/gittensory-ui/public/openapi.json # src/openapi/schemas.ts # src/services/decision-pack.ts
|
@JSONbored please review the change |
JSONbored
left a comment
There was a problem hiding this comment.
@MkDev11 please fix merge conflicts.
fixed |
JSONbored
left a comment
There was a problem hiding this comment.
@MkDev11 the branch is clean against current main and CI is green, but it conflicts with #219 since it just landed.
Required change:
- Rebase after the #219 merge-order decision.
- Regenerate OpenAPI artifacts and rerun the decision-pack/outcome tests.
Validation expected:
- npm run test:ci
- npm run ui:openapi:check
fixed |
…-outcome-feedback # Conflicts: # src/services/decision-pack.ts
…-outcome-feedback
…-outcome-feedback
JSONbored
left a comment
There was a problem hiding this comment.
@MkDev11
A few notes:
- This is the right closed-loop direction for #96: Gittensory should learn from deterministic outcomes, not opaque scoring claims.
- The implementation separates merged/closed/improved/stale/ignored outcomes and keeps maintainer-lane outcomes out of normal contributor feedback.
- The tests cover idempotent outcome updates, stale/ignored classification, maintainer associations, OpenAPI shape, and decision-pack feedback impact.
No code changes requested.
Summary
Closes #96
Scope
CONTRIBUTING.mdand does not reintroduce GitHub Pages, VitePress,site/, orCNAME.Validation
npm run test:ci/ CIvalidateon headde717a2git diff --checknpm run actionlintnpm run typechecknpm run test:coveragelocally; 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: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:
validatepassed on the current PR head.Safety
Notes
0017_agent_recommendation_outcomes.sqlfor deterministic recommendation outcome storage.