@thinkai/tai-api-contract 2.63.0 → 2.65.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -1,7 +1,7 @@
1
1
  openapi: 3.0.3
2
2
  info:
3
3
  title: ThinkAI API
4
- version: 2.63.0
4
+ version: 2.65.0
5
5
  description: >
6
6
  Contract surface for the AI Driven SDLC backend used by ThinkAI.
7
7
  Workspace-scoped routes use `/workspaces/{workspaceId}/...`.
@@ -56,6 +56,12 @@ tags:
56
56
  description: Your AI Journey dashboard aggregators (epic #267); composes readiness, metrics, and AI-spend data for the /dashboard page
57
57
  - name: SdlcInsights
58
58
  description: SDLC Engineering Health insights — DORA, CI, collaboration, security, governance, and operational metrics for `/insights/sdlc/overview`.
59
+ - name: InsightsRecommendations
60
+ description: >
61
+ Cross-analysis Insights & Recommendations page (`/insights-recommendations`).
62
+ Seven workspace-scoped GET resources under `/insights/recommendations-page/*`
63
+ with shared date/org/provider filters. Numbers are computed from dashboard metrics,
64
+ AI-tool usage/spend, org chart, and readiness — never mock payloads.
59
65
  - name: WorkspaceActivity
60
66
  description: Workspace-wide lifecycle activity log — repo add/remove/re-add and integration lifecycle events (issue #515).
61
67
  - name: PlatformAdmin
@@ -2721,6 +2727,303 @@ paths:
2721
2727
  "404":
2722
2728
  description: Workspace or metric not found
2723
2729
 
2730
+ /workspaces/{workspaceId}/insights/recommendations-page/verdict-summary:
2731
+ get:
2732
+ tags: [InsightsRecommendations]
2733
+ summary: Insights recommendations — verdict summary cards
2734
+ description: >
2735
+ Top status cards for `/insights-recommendations`. `productivityMultiplier` is the
2736
+ merged-PR volume ratio versus the previous equal-length window (not AI-active vs
2737
+ non-users). `ciFailRatePct` is portfolio CI fail rate percent. Cost statuses compare
2738
+ AI spend per merged PR to the prior window. Status thresholds match the product UI:
2739
+ productivity ≥1.5 healthy / ≥1.0 watch / else at-risk; CI fail <15 healthy / ≤30 watch /
2740
+ else at-risk; cost change ≤0 healthy / <20% watch / else at-risk (prev≤0 → watch).
2741
+ operationId: getInsightsRecommendationsVerdictSummary
2742
+ parameters:
2743
+ - $ref: "#/components/parameters/WorkspaceId"
2744
+ - $ref: "#/components/parameters/InsightsRecommendationsDepartments"
2745
+ - $ref: "#/components/parameters/InsightsRecommendationsTeams"
2746
+ - $ref: "#/components/parameters/InsightsRecommendationsDateFrom"
2747
+ - $ref: "#/components/parameters/InsightsRecommendationsDateTo"
2748
+ - $ref: "#/components/parameters/InsightsRecommendationsProvider"
2749
+ - $ref: "#/components/parameters/InsightsRecommendationsIncludeUnmapped"
2750
+ - $ref: "#/components/parameters/InsightsRecommendationsIncludeBots"
2751
+ responses:
2752
+ "200":
2753
+ description: Verdict summary with `_meta.isMock=false`
2754
+ content:
2755
+ application/json:
2756
+ schema:
2757
+ $ref: "#/components/schemas/InsightsRecommendationsVerdictSummaryResponse"
2758
+ "400":
2759
+ description: Invalid filter or date range
2760
+ content:
2761
+ application/json:
2762
+ schema:
2763
+ type: object
2764
+ required: [error]
2765
+ properties:
2766
+ error: { type: string, example: invalid_query }
2767
+ "401":
2768
+ $ref: "#/components/responses/Unauthorized"
2769
+ "403":
2770
+ $ref: "#/components/responses/Forbidden"
2771
+ "404":
2772
+ description: Workspace does not exist or malformed workspaceId
2773
+
2774
+ /workspaces/{workspaceId}/insights/recommendations-page/roi-quadrant:
2775
+ get:
2776
+ tags: [InsightsRecommendations]
2777
+ summary: Insights recommendations — AI spend vs delivery output
2778
+ description: >
2779
+ Per-engineer AI spend (USD) and merged PR counts for the scatter / ROI quadrant.
2780
+ Includes engineers with spend>0 or mergedPrs>0. Medians are over the returned set
2781
+ (n<2 uses the single point or zeros when empty). `id` is the stable org person id.
2782
+ operationId: getInsightsRecommendationsRoiQuadrant
2783
+ parameters:
2784
+ - $ref: "#/components/parameters/WorkspaceId"
2785
+ - $ref: "#/components/parameters/InsightsRecommendationsDepartments"
2786
+ - $ref: "#/components/parameters/InsightsRecommendationsTeams"
2787
+ - $ref: "#/components/parameters/InsightsRecommendationsDateFrom"
2788
+ - $ref: "#/components/parameters/InsightsRecommendationsDateTo"
2789
+ - $ref: "#/components/parameters/InsightsRecommendationsProvider"
2790
+ - $ref: "#/components/parameters/InsightsRecommendationsIncludeUnmapped"
2791
+ - $ref: "#/components/parameters/InsightsRecommendationsIncludeBots"
2792
+ responses:
2793
+ "200":
2794
+ description: ROI quadrant payload
2795
+ content:
2796
+ application/json:
2797
+ schema:
2798
+ $ref: "#/components/schemas/InsightsRecommendationsRoiQuadrantResponse"
2799
+ "400":
2800
+ description: Invalid filter or date range
2801
+ content:
2802
+ application/json:
2803
+ schema:
2804
+ type: object
2805
+ required: [error]
2806
+ properties:
2807
+ error: { type: string, example: invalid_query }
2808
+ "401":
2809
+ $ref: "#/components/responses/Unauthorized"
2810
+ "403":
2811
+ $ref: "#/components/responses/Forbidden"
2812
+ "404":
2813
+ description: Workspace does not exist or malformed workspaceId
2814
+
2815
+ /workspaces/{workspaceId}/insights/recommendations-page/velocity-quality:
2816
+ get:
2817
+ tags: [InsightsRecommendations]
2818
+ summary: Insights recommendations — velocity vs quality weekly series
2819
+ description: >
2820
+ Weekly merged PR volume and CI fail rate for the filter window. Weeks follow the
2821
+ Measure calendar-week bucket convention. `targetFailRatePct` is the org target (15).
2822
+ `volumeGrowthPct` is percent change vs the prior equal-length period.
2823
+ operationId: getInsightsRecommendationsVelocityQuality
2824
+ parameters:
2825
+ - $ref: "#/components/parameters/WorkspaceId"
2826
+ - $ref: "#/components/parameters/InsightsRecommendationsDepartments"
2827
+ - $ref: "#/components/parameters/InsightsRecommendationsTeams"
2828
+ - $ref: "#/components/parameters/InsightsRecommendationsDateFrom"
2829
+ - $ref: "#/components/parameters/InsightsRecommendationsDateTo"
2830
+ - $ref: "#/components/parameters/InsightsRecommendationsProvider"
2831
+ - $ref: "#/components/parameters/InsightsRecommendationsIncludeUnmapped"
2832
+ - $ref: "#/components/parameters/InsightsRecommendationsIncludeBots"
2833
+ responses:
2834
+ "200":
2835
+ description: Velocity vs quality payload
2836
+ content:
2837
+ application/json:
2838
+ schema:
2839
+ $ref: "#/components/schemas/InsightsRecommendationsVelocityQualityResponse"
2840
+ "400":
2841
+ description: Invalid filter or date range
2842
+ content:
2843
+ application/json:
2844
+ schema:
2845
+ type: object
2846
+ required: [error]
2847
+ properties:
2848
+ error: { type: string, example: invalid_query }
2849
+ "401":
2850
+ $ref: "#/components/responses/Unauthorized"
2851
+ "403":
2852
+ $ref: "#/components/responses/Forbidden"
2853
+ "404":
2854
+ description: Workspace does not exist or malformed workspaceId
2855
+
2856
+ /workspaces/{workspaceId}/insights/recommendations-page/review-pipeline:
2857
+ get:
2858
+ tags: [InsightsRecommendations]
2859
+ summary: Insights recommendations — review bottleneck funnel
2860
+ description: >
2861
+ Review funnel stages `open-review`, `review-approval`, `approval-merge`, and `merged`.
2862
+ Wait stages use delivery-health hours (`avgReviewWaitHours`, `avgApprovalHours`, and
2863
+ approval→merge as max(0, avgTimeToMergeHours − wait − approval)). The `merged` stage
2864
+ uses `medianHours=0` (volume is not hours). `bottleneckStageId` is the wait stage with
2865
+ the highest median hours. `reviewerLoad` comes from persisted `deliveryHealth.topReviewers`
2866
+ when available; otherwise an empty array (never invented reviewers).
2867
+ operationId: getInsightsRecommendationsReviewPipeline
2868
+ parameters:
2869
+ - $ref: "#/components/parameters/WorkspaceId"
2870
+ - $ref: "#/components/parameters/InsightsRecommendationsDepartments"
2871
+ - $ref: "#/components/parameters/InsightsRecommendationsTeams"
2872
+ - $ref: "#/components/parameters/InsightsRecommendationsDateFrom"
2873
+ - $ref: "#/components/parameters/InsightsRecommendationsDateTo"
2874
+ - $ref: "#/components/parameters/InsightsRecommendationsProvider"
2875
+ - $ref: "#/components/parameters/InsightsRecommendationsIncludeUnmapped"
2876
+ - $ref: "#/components/parameters/InsightsRecommendationsIncludeBots"
2877
+ responses:
2878
+ "200":
2879
+ description: Review pipeline payload
2880
+ content:
2881
+ application/json:
2882
+ schema:
2883
+ $ref: "#/components/schemas/InsightsRecommendationsReviewPipelineResponse"
2884
+ "400":
2885
+ description: Invalid filter or date range
2886
+ content:
2887
+ application/json:
2888
+ schema:
2889
+ type: object
2890
+ required: [error]
2891
+ properties:
2892
+ error: { type: string, example: invalid_query }
2893
+ "401":
2894
+ $ref: "#/components/responses/Unauthorized"
2895
+ "403":
2896
+ $ref: "#/components/responses/Forbidden"
2897
+ "404":
2898
+ description: Workspace does not exist or malformed workspaceId
2899
+
2900
+ /workspaces/{workspaceId}/insights/recommendations-page/cost-per-pr:
2901
+ get:
2902
+ tags: [InsightsRecommendations]
2903
+ summary: Insights recommendations — cost per merged PR by team
2904
+ description: >
2905
+ Org and per-team AI spend / merged PRs. Teams with zero merged PRs in the window are
2906
+ omitted. `costPerPrUsd = spendUsd / mergedPrs`. `activeAi` is distinct AI-active members
2907
+ on the team in the window (provider filter applied).
2908
+ operationId: getInsightsRecommendationsCostPerPr
2909
+ parameters:
2910
+ - $ref: "#/components/parameters/WorkspaceId"
2911
+ - $ref: "#/components/parameters/InsightsRecommendationsDepartments"
2912
+ - $ref: "#/components/parameters/InsightsRecommendationsTeams"
2913
+ - $ref: "#/components/parameters/InsightsRecommendationsDateFrom"
2914
+ - $ref: "#/components/parameters/InsightsRecommendationsDateTo"
2915
+ - $ref: "#/components/parameters/InsightsRecommendationsProvider"
2916
+ - $ref: "#/components/parameters/InsightsRecommendationsIncludeUnmapped"
2917
+ - $ref: "#/components/parameters/InsightsRecommendationsIncludeBots"
2918
+ responses:
2919
+ "200":
2920
+ description: Cost per PR payload
2921
+ content:
2922
+ application/json:
2923
+ schema:
2924
+ $ref: "#/components/schemas/InsightsRecommendationsCostPerPrResponse"
2925
+ "400":
2926
+ description: Invalid filter or date range
2927
+ content:
2928
+ application/json:
2929
+ schema:
2930
+ type: object
2931
+ required: [error]
2932
+ properties:
2933
+ error: { type: string, example: invalid_query }
2934
+ "401":
2935
+ $ref: "#/components/responses/Unauthorized"
2936
+ "403":
2937
+ $ref: "#/components/responses/Forbidden"
2938
+ "404":
2939
+ description: Workspace does not exist or malformed workspaceId
2940
+
2941
+ /workspaces/{workspaceId}/insights/recommendations-page/adoption-gap:
2942
+ get:
2943
+ tags: [InsightsRecommendations]
2944
+ summary: Insights recommendations — AI adoption gap by team
2945
+ description: >
2946
+ Headcount vs AI-active contributors and per-team readiness/output. `readinessPct` is the
2947
+ mean latest overallScore of repos with matching `teamId` (workspace mean fallback).
2948
+ `outputPerEngPerWeek` is team merged PRs / headcount / weeks in window. `upliftPct`
2949
+ estimates additional PR volume if non-adopters matched leader-team output
2950
+ (teams with >50% adoption).
2951
+ operationId: getInsightsRecommendationsAdoptionGap
2952
+ parameters:
2953
+ - $ref: "#/components/parameters/WorkspaceId"
2954
+ - $ref: "#/components/parameters/InsightsRecommendationsDepartments"
2955
+ - $ref: "#/components/parameters/InsightsRecommendationsTeams"
2956
+ - $ref: "#/components/parameters/InsightsRecommendationsDateFrom"
2957
+ - $ref: "#/components/parameters/InsightsRecommendationsDateTo"
2958
+ - $ref: "#/components/parameters/InsightsRecommendationsProvider"
2959
+ - $ref: "#/components/parameters/InsightsRecommendationsIncludeUnmapped"
2960
+ - $ref: "#/components/parameters/InsightsRecommendationsIncludeBots"
2961
+ responses:
2962
+ "200":
2963
+ description: Adoption gap payload
2964
+ content:
2965
+ application/json:
2966
+ schema:
2967
+ $ref: "#/components/schemas/InsightsRecommendationsAdoptionGapResponse"
2968
+ "400":
2969
+ description: Invalid filter or date range
2970
+ content:
2971
+ application/json:
2972
+ schema:
2973
+ type: object
2974
+ required: [error]
2975
+ properties:
2976
+ error: { type: string, example: invalid_query }
2977
+ "401":
2978
+ $ref: "#/components/responses/Unauthorized"
2979
+ "403":
2980
+ $ref: "#/components/responses/Forbidden"
2981
+ "404":
2982
+ description: Workspace does not exist or malformed workspaceId
2983
+
2984
+ /workspaces/{workspaceId}/insights/recommendations-page/recommendations:
2985
+ get:
2986
+ tags: [InsightsRecommendations]
2987
+ summary: Insights recommendations — deterministic action items
2988
+ description: >
2989
+ Deterministic rules engine (no LLM). Emits module-anchored recommendations with
2990
+ `moduleId` in {mod-spend-vs-output, mod-velocity-vs-quality, mod-review-bottleneck,
2991
+ mod-cost-per-pr, mod-adoption-gap}. Action items must cite one AI metric and one
2992
+ delivery metric in `observed`. Evidence chips use kinds user-spend|team|contributor|repo|module
2993
+ with real target ids when available.
2994
+ operationId: getInsightsRecommendationsRecommendations
2995
+ parameters:
2996
+ - $ref: "#/components/parameters/WorkspaceId"
2997
+ - $ref: "#/components/parameters/InsightsRecommendationsDepartments"
2998
+ - $ref: "#/components/parameters/InsightsRecommendationsTeams"
2999
+ - $ref: "#/components/parameters/InsightsRecommendationsDateFrom"
3000
+ - $ref: "#/components/parameters/InsightsRecommendationsDateTo"
3001
+ - $ref: "#/components/parameters/InsightsRecommendationsProvider"
3002
+ - $ref: "#/components/parameters/InsightsRecommendationsIncludeUnmapped"
3003
+ - $ref: "#/components/parameters/InsightsRecommendationsIncludeBots"
3004
+ responses:
3005
+ "200":
3006
+ description: Recommendation items
3007
+ content:
3008
+ application/json:
3009
+ schema:
3010
+ $ref: "#/components/schemas/InsightsRecommendationsListResponse"
3011
+ "400":
3012
+ description: Invalid filter or date range
3013
+ content:
3014
+ application/json:
3015
+ schema:
3016
+ type: object
3017
+ required: [error]
3018
+ properties:
3019
+ error: { type: string, example: invalid_query }
3020
+ "401":
3021
+ $ref: "#/components/responses/Unauthorized"
3022
+ "403":
3023
+ $ref: "#/components/responses/Forbidden"
3024
+ "404":
3025
+ description: Workspace does not exist or malformed workspaceId
3026
+
2724
3027
  /workspaces/{workspaceId}/integrations/ai-tool/{provider}/members:
2725
3028
  get:
2726
3029
  tags: [Workspace]
@@ -7218,6 +7521,71 @@ components:
7218
7521
  schema:
7219
7522
  type: string
7220
7523
  default: All departments
7524
+ InsightsRecommendationsDepartments:
7525
+ name: departments
7526
+ in: query
7527
+ description: >
7528
+ Org-chart department ids (repeat param or comma-separated). Empty/omitted = all departments.
7529
+ schema:
7530
+ type: array
7531
+ items:
7532
+ type: string
7533
+ style: form
7534
+ explode: true
7535
+ InsightsRecommendationsTeams:
7536
+ name: teams
7537
+ in: query
7538
+ description: >
7539
+ Org-chart team ids (repeat param or comma-separated). Empty/omitted = all teams in department scope.
7540
+ schema:
7541
+ type: array
7542
+ items:
7543
+ type: string
7544
+ style: form
7545
+ explode: true
7546
+ InsightsRecommendationsDateFrom:
7547
+ name: dateFrom
7548
+ in: query
7549
+ description: >
7550
+ Inclusive window start (`YYYY-MM-DD`). Required with `dateTo`. Maps to Measure day-mode
7551
+ custom range (`from`/`to`). Max span 180 days.
7552
+ required: true
7553
+ schema:
7554
+ type: string
7555
+ format: date
7556
+ InsightsRecommendationsDateTo:
7557
+ name: dateTo
7558
+ in: query
7559
+ description: Inclusive window end (`YYYY-MM-DD`). Required with `dateFrom`.
7560
+ required: true
7561
+ schema:
7562
+ type: string
7563
+ format: date
7564
+ InsightsRecommendationsProvider:
7565
+ name: provider
7566
+ in: query
7567
+ description: >
7568
+ AI provider ids (e.g. `cursor`, `copilot`). Empty/omitted = all configured providers.
7569
+ schema:
7570
+ type: array
7571
+ items:
7572
+ type: string
7573
+ style: form
7574
+ explode: true
7575
+ InsightsRecommendationsIncludeUnmapped:
7576
+ name: includeUnmapped
7577
+ in: query
7578
+ description: Include contributors / AI rows not mapped to a team.
7579
+ schema:
7580
+ type: boolean
7581
+ default: true
7582
+ InsightsRecommendationsIncludeBots:
7583
+ name: includeBots
7584
+ in: query
7585
+ description: Include bot accounts in PR/CI stats.
7586
+ schema:
7587
+ type: boolean
7588
+ default: false
7221
7589
  PaginationLimit:
7222
7590
  name: limit
7223
7591
  in: query
@@ -11495,6 +11863,27 @@ components:
11495
11863
  productivityRefreshInProgress:
11496
11864
  type: boolean
11497
11865
  description: True while another session holds the workspace productivity ingest advisory lock.
11866
+ productivityIngestPendingRepoCount:
11867
+ type: integer
11868
+ minimum: 0
11869
+ nullable: true
11870
+ description: Repos remaining in the staged backfill queue.
11871
+ productivityIngestSkippedRepoCount:
11872
+ type: integer
11873
+ minimum: 0
11874
+ nullable: true
11875
+ description: Repos benign-skipped or quarantined during backfill.
11876
+ productivityIngestFailedRepoCount:
11877
+ type: integer
11878
+ minimum: 0
11879
+ nullable: true
11880
+ description: Repos with transient failures awaiting retry in the current phase.
11881
+ productivityIngestCoveragePercent:
11882
+ type: number
11883
+ minimum: 0
11884
+ maximum: 100
11885
+ nullable: true
11886
+ description: Percent of eligible repos with merged-PR productivity data (excludes skipped/quarantined).
11498
11887
 
11499
11888
  GithubInstallUrlRequestDto:
11500
11889
  type: object
@@ -13899,6 +14288,8 @@ components:
13899
14288
  - productivity.ingest.completed
13900
14289
  - productivity.ingest.failed
13901
14290
  - productivity.ingest.skipped
14291
+ - productivity.ingest.repo_skipped
14292
+ - productivity.ingest.coverage_gap_detected
13902
14293
  - integrations.newrelic.added
13903
14294
  - integrations.newrelic.removed
13904
14295
  - integrations.newrelic.sync_completed
@@ -14503,3 +14894,317 @@ components:
14503
14894
  $ref: "#/components/schemas/SdlcDrilldownItemDto"
14504
14895
  nextCursor:
14505
14896
  type: string
14897
+
14898
+ InsightsResponseMeta:
14899
+ type: object
14900
+ required: [syncedAt, isMock]
14901
+ properties:
14902
+ syncedAt:
14903
+ type: string
14904
+ format: date-time
14905
+ description: ISO timestamp when numbers were computed server-side
14906
+ isMock:
14907
+ type: boolean
14908
+ description: Must be false for production handlers
14909
+ enum: [false]
14910
+
14911
+ InsightsVerdictStatus:
14912
+ type: string
14913
+ enum: [healthy, watch, at-risk]
14914
+
14915
+ InsightsRecommendationsVerdictSummary:
14916
+ type: object
14917
+ required:
14918
+ - productivityMultiplier
14919
+ - productivityStatus
14920
+ - ciFailRatePct
14921
+ - qualityStatus
14922
+ - spendPerMergedPrUsd
14923
+ - spendPerMergedPrPrevUsd
14924
+ - costStatus
14925
+ properties:
14926
+ productivityMultiplier:
14927
+ type: number
14928
+ description: >
14929
+ Merged-PR volume ratio vs prior equal-length window (current/prior).
14930
+ When priorMergedPrs is 0, value is 1.0 and productivityStatus is watch.
14931
+ productivityStatus:
14932
+ $ref: "#/components/schemas/InsightsVerdictStatus"
14933
+ ciFailRatePct:
14934
+ type: number
14935
+ qualityStatus:
14936
+ $ref: "#/components/schemas/InsightsVerdictStatus"
14937
+ spendPerMergedPrUsd:
14938
+ type: number
14939
+ spendPerMergedPrPrevUsd:
14940
+ type: number
14941
+ costStatus:
14942
+ $ref: "#/components/schemas/InsightsVerdictStatus"
14943
+
14944
+ InsightsRecommendationsVerdictSummaryResponse:
14945
+ allOf:
14946
+ - $ref: "#/components/schemas/InsightsRecommendationsVerdictSummary"
14947
+ - type: object
14948
+ required: [_meta]
14949
+ properties:
14950
+ _meta:
14951
+ $ref: "#/components/schemas/InsightsResponseMeta"
14952
+
14953
+ InsightsRoiEngineerPoint:
14954
+ type: object
14955
+ required: [id, name, team, spendUsd, mergedPrs]
14956
+ properties:
14957
+ id: { type: string }
14958
+ name: { type: string }
14959
+ team: { type: string }
14960
+ spendUsd: { type: number }
14961
+ mergedPrs: { type: number }
14962
+
14963
+ InsightsRecommendationsRoiQuadrant:
14964
+ type: object
14965
+ required: [medianSpendUsd, medianMergedPrs, engineers, correlationHint]
14966
+ properties:
14967
+ medianSpendUsd: { type: number }
14968
+ medianMergedPrs: { type: number }
14969
+ engineers:
14970
+ type: array
14971
+ items:
14972
+ $ref: "#/components/schemas/InsightsRoiEngineerPoint"
14973
+ correlationHint: { type: string }
14974
+
14975
+ InsightsRecommendationsRoiQuadrantResponse:
14976
+ allOf:
14977
+ - $ref: "#/components/schemas/InsightsRecommendationsRoiQuadrant"
14978
+ - type: object
14979
+ required: [_meta]
14980
+ properties:
14981
+ _meta:
14982
+ $ref: "#/components/schemas/InsightsResponseMeta"
14983
+
14984
+ InsightsVelocityQualityWeek:
14985
+ type: object
14986
+ required: [weekStartIso, mergedPrs, ciFailRatePct]
14987
+ properties:
14988
+ weekStartIso: { type: string, format: date }
14989
+ mergedPrs: { type: number }
14990
+ ciFailRatePct: { type: number }
14991
+
14992
+ InsightsRecommendationsVelocityQuality:
14993
+ type: object
14994
+ required: [weeks, targetFailRatePct, volumeGrowthPct]
14995
+ properties:
14996
+ weeks:
14997
+ type: array
14998
+ items:
14999
+ $ref: "#/components/schemas/InsightsVelocityQualityWeek"
15000
+ targetFailRatePct: { type: number }
15001
+ volumeGrowthPct: { type: number }
15002
+
15003
+ InsightsRecommendationsVelocityQualityResponse:
15004
+ allOf:
15005
+ - $ref: "#/components/schemas/InsightsRecommendationsVelocityQuality"
15006
+ - type: object
15007
+ required: [_meta]
15008
+ properties:
15009
+ _meta:
15010
+ $ref: "#/components/schemas/InsightsResponseMeta"
15011
+
15012
+ InsightsReviewStageId:
15013
+ type: string
15014
+ enum: [open-review, review-approval, approval-merge, merged]
15015
+
15016
+ InsightsReviewStage:
15017
+ type: object
15018
+ required: [id, name, medianHours, trendPct]
15019
+ properties:
15020
+ id:
15021
+ $ref: "#/components/schemas/InsightsReviewStageId"
15022
+ name: { type: string }
15023
+ medianHours:
15024
+ type: number
15025
+ description: Wait hours for funnel stages; 0 for the merged volume stage
15026
+ trendPct: { type: number }
15027
+
15028
+ InsightsReviewerLoadItem:
15029
+ type: object
15030
+ required: [id, name, reviewsCount, sharePct]
15031
+ properties:
15032
+ id: { type: string }
15033
+ name: { type: string }
15034
+ reviewsCount: { type: number }
15035
+ sharePct: { type: number }
15036
+
15037
+ InsightsRecommendationsReviewPipeline:
15038
+ type: object
15039
+ required: [stages, bottleneckStageId, reviewerLoad]
15040
+ properties:
15041
+ stages:
15042
+ type: array
15043
+ items:
15044
+ $ref: "#/components/schemas/InsightsReviewStage"
15045
+ bottleneckStageId:
15046
+ $ref: "#/components/schemas/InsightsReviewStageId"
15047
+ reviewerLoad:
15048
+ type: array
15049
+ items:
15050
+ $ref: "#/components/schemas/InsightsReviewerLoadItem"
15051
+
15052
+ InsightsRecommendationsReviewPipelineResponse:
15053
+ allOf:
15054
+ - $ref: "#/components/schemas/InsightsRecommendationsReviewPipeline"
15055
+ - type: object
15056
+ required: [_meta]
15057
+ properties:
15058
+ _meta:
15059
+ $ref: "#/components/schemas/InsightsResponseMeta"
15060
+
15061
+ InsightsCostPerPrTeam:
15062
+ type: object
15063
+ required: [id, name, activeAi, spendUsd, mergedPrs, costPerPrUsd, changePct]
15064
+ properties:
15065
+ id: { type: string }
15066
+ name: { type: string }
15067
+ activeAi: { type: number }
15068
+ spendUsd: { type: number }
15069
+ mergedPrs: { type: number }
15070
+ costPerPrUsd: { type: number }
15071
+ changePct: { type: number }
15072
+
15073
+ InsightsRecommendationsCostPerPr:
15074
+ type: object
15075
+ required: [orgAvgCostUsd, orgAvgPrevUsd, teams]
15076
+ properties:
15077
+ orgAvgCostUsd: { type: number }
15078
+ orgAvgPrevUsd: { type: number }
15079
+ teams:
15080
+ type: array
15081
+ items:
15082
+ $ref: "#/components/schemas/InsightsCostPerPrTeam"
15083
+
15084
+ InsightsRecommendationsCostPerPrResponse:
15085
+ allOf:
15086
+ - $ref: "#/components/schemas/InsightsRecommendationsCostPerPr"
15087
+ - type: object
15088
+ required: [_meta]
15089
+ properties:
15090
+ _meta:
15091
+ $ref: "#/components/schemas/InsightsResponseMeta"
15092
+
15093
+ InsightsAdoptionTeam:
15094
+ type: object
15095
+ required: [id, name, activeAi, totalMembers, readinessPct, outputPerEngPerWeek]
15096
+ properties:
15097
+ id: { type: string }
15098
+ name: { type: string }
15099
+ activeAi: { type: number }
15100
+ totalMembers: { type: number }
15101
+ readinessPct: { type: number }
15102
+ outputPerEngPerWeek: { type: number }
15103
+
15104
+ InsightsRecommendationsAdoptionGap:
15105
+ type: object
15106
+ required: [totalContributors, activeContributors, upliftPct, teams]
15107
+ properties:
15108
+ totalContributors: { type: number }
15109
+ activeContributors: { type: number }
15110
+ upliftPct: { type: number }
15111
+ teams:
15112
+ type: array
15113
+ items:
15114
+ $ref: "#/components/schemas/InsightsAdoptionTeam"
15115
+
15116
+ InsightsRecommendationsAdoptionGapResponse:
15117
+ allOf:
15118
+ - $ref: "#/components/schemas/InsightsRecommendationsAdoptionGap"
15119
+ - type: object
15120
+ required: [_meta]
15121
+ properties:
15122
+ _meta:
15123
+ $ref: "#/components/schemas/InsightsResponseMeta"
15124
+
15125
+ InsightsRecommendationModuleId:
15126
+ type: string
15127
+ enum:
15128
+ - mod-spend-vs-output
15129
+ - mod-velocity-vs-quality
15130
+ - mod-review-bottleneck
15131
+ - mod-cost-per-pr
15132
+ - mod-adoption-gap
15133
+
15134
+ InsightsRecommendationImpact:
15135
+ type: string
15136
+ enum: [high, medium]
15137
+
15138
+ InsightsRecommendationTone:
15139
+ type: string
15140
+ enum: [action, no-action]
15141
+
15142
+ InsightsEvidenceLinkKind:
15143
+ type: string
15144
+ enum: [user-spend, team, contributor, repo, module]
15145
+
15146
+ InsightsEvidenceChip:
15147
+ type: object
15148
+ required: [label, kind, targetId]
15149
+ properties:
15150
+ label: { type: string }
15151
+ kind:
15152
+ $ref: "#/components/schemas/InsightsEvidenceLinkKind"
15153
+ targetId: { type: string }
15154
+
15155
+ InsightsRecommendationEvidenceGroup:
15156
+ type: object
15157
+ required: [title, chips]
15158
+ properties:
15159
+ title: { type: string }
15160
+ chips:
15161
+ type: array
15162
+ items:
15163
+ $ref: "#/components/schemas/InsightsEvidenceChip"
15164
+
15165
+ InsightsRecommendationDto:
15166
+ type: object
15167
+ required:
15168
+ - moduleId
15169
+ - moduleName
15170
+ - tone
15171
+ - impact
15172
+ - headline
15173
+ - observed
15174
+ - action
15175
+ - benefit
15176
+ - confidencePct
15177
+ - evidence
15178
+ properties:
15179
+ moduleId:
15180
+ $ref: "#/components/schemas/InsightsRecommendationModuleId"
15181
+ moduleName: { type: string }
15182
+ tone:
15183
+ $ref: "#/components/schemas/InsightsRecommendationTone"
15184
+ impact:
15185
+ $ref: "#/components/schemas/InsightsRecommendationImpact"
15186
+ headline: { type: string }
15187
+ observed:
15188
+ type: string
15189
+ description: For tone=action must cite one AI metric and one delivery metric
15190
+ action: { type: string }
15191
+ benefit: { type: string }
15192
+ confidencePct:
15193
+ type: number
15194
+ minimum: 0
15195
+ maximum: 100
15196
+ evidence:
15197
+ type: array
15198
+ items:
15199
+ $ref: "#/components/schemas/InsightsRecommendationEvidenceGroup"
15200
+
15201
+ InsightsRecommendationsListResponse:
15202
+ type: object
15203
+ required: [items, _meta]
15204
+ properties:
15205
+ items:
15206
+ type: array
15207
+ items:
15208
+ $ref: "#/components/schemas/InsightsRecommendationDto"
15209
+ _meta:
15210
+ $ref: "#/components/schemas/InsightsResponseMeta"