@thinkai/tai-api-contract 2.5.1 → 2.5.16

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.5.1
4
+ version: 2.5.16
5
5
  description: >
6
6
  Contract surface for the AI Driven SDLC backend used by ThinkAI.
7
7
  Workspace-scoped routes use `/workspaces/{workspaceId}/...`.
@@ -574,587 +574,779 @@ paths:
574
574
  "401":
575
575
  $ref: "#/components/responses/Unauthorized"
576
576
 
577
- /workspaces/{workspaceId}/company-profile:
577
+ /workspaces/{workspaceId}/insights/productivity-insights/aggregated-teams:
578
578
  get:
579
- tags: [CompanyProfile]
580
- summary: Get company profile
581
- operationId: getCompanyProfile
579
+ tags: [Workspace]
580
+ summary: Productivity insights — aggregated team weekly metrics
581
+ description: >
582
+ Team-level rollup for Productivity insights (Aggregated Teams view on /phase-c/c4).
583
+ Returns weekly merged PR counts and fail rates per engineering team, filtered by the
584
+ same department, team, and range controls as the Measure UI.
585
+ operationId: getProductivityInsightsAggregatedTeams
582
586
  parameters:
583
587
  - $ref: "#/components/parameters/WorkspaceId"
588
+ - name: rangeId
589
+ in: query
590
+ description: Measure time range (maps to weekly bucket count).
591
+ schema:
592
+ type: string
593
+ enum: [4w, 8w, q]
594
+ default: 4w
595
+ - $ref: "#/components/parameters/InsightsDepartmentFilter"
596
+ - name: team
597
+ in: query
598
+ description: Team name filter; send `All teams` to include every team.
599
+ schema:
600
+ type: string
601
+ default: All teams
602
+ - name: windowEnd
603
+ in: query
604
+ description: ISO timestamp for the end of the last weekly bucket; omit for current week.
605
+ schema:
606
+ type: string
607
+ format: date-time
584
608
  responses:
585
609
  "200":
586
- description: >
587
- Company profile DTO, or JSON `null` when the workspace exists but no profile
588
- has been saved yet. Clients should treat `null` as the empty initial state
589
- (the wizard renders blank inputs); they MUST NOT treat the absence of a
590
- saved profile as a 404.
610
+ description: Aggregated team productivity series
591
611
  content:
592
612
  application/json:
593
613
  schema:
594
- $ref: "#/components/schemas/CompanyProfileDto"
595
- examples:
596
- sample:
597
- summary: Saved profile
598
- value:
599
- companyName: Acme
600
- industry: software
601
- companySize: "51-200"
602
- engineeringHC: "42"
603
- headquarters: "Austin, TX"
614
+ $ref: "#/components/schemas/ProductivityInsightsAggregatedTeamsDto"
604
615
  "400":
605
- description: Malformed workspaceId
616
+ description: Invalid query parameters
606
617
  content:
607
618
  application/json:
608
619
  schema:
609
- $ref: "#/components/schemas/ErrorMessageDto"
620
+ type: object
621
+ required: [error]
622
+ properties:
623
+ error: { type: string, example: invalid_query }
610
624
  "401":
611
625
  $ref: "#/components/responses/Unauthorized"
612
626
  "403":
613
627
  $ref: "#/components/responses/Forbidden"
614
628
  "404":
615
- description: Workspace does not exist
616
- content:
617
- application/json:
618
- schema:
619
- $ref: "#/components/schemas/ErrorMessageDto"
620
- put:
621
- tags: [CompanyProfile]
622
- summary: Upsert company profile
623
- operationId: putCompanyProfile
629
+ description: Workspace does not exist or malformed workspaceId
630
+
631
+ /workspaces/{workspaceId}/insights/productivity-insights/merged-pr-trends:
632
+ get:
633
+ tags: [Workspace]
634
+ summary: Productivity insights — merged PR trends (portfolio + contributors)
624
635
  description: >
625
- Idempotent upsert of the workspace's company profile. On success, also
626
- synchronizes `tai_workspaces.name` to the new `companyName` so subsequent
627
- calls to `GET /me/workspaces` reflect the updated display name. Repeated
628
- PUTs replace the stored payload entirely.
636
+ Weekly merged PR counts for the Measure "Merged PR Trends" widget on /phase-c/c4
637
+ Individual Contributors. Returns a portfolio aggregate series plus per-contributor weekly
638
+ counts, filtered by the same department, team, and range controls as the Measure UI.
639
+ operationId: getProductivityInsightsMergedPrTrends
629
640
  parameters:
630
641
  - $ref: "#/components/parameters/WorkspaceId"
631
- requestBody:
632
- required: true
633
- content:
634
- application/json:
635
- schema:
636
- $ref: "#/components/schemas/CompanyProfileDto"
642
+ - name: rangeId
643
+ in: query
644
+ description: Measure time range (maps to weekly bucket count).
645
+ schema:
646
+ type: string
647
+ enum: [4w, 8w, q]
648
+ default: 4w
649
+ - $ref: "#/components/parameters/InsightsDepartmentFilter"
650
+ - name: team
651
+ in: query
652
+ description: Team name filter; send `All teams` to include every team.
653
+ schema:
654
+ type: string
655
+ default: All teams
656
+ - name: windowEnd
657
+ in: query
658
+ description: ISO timestamp for the end of the last weekly bucket; omit for current week.
659
+ schema:
660
+ type: string
661
+ format: date-time
637
662
  responses:
638
- "204":
639
- description: Saved
663
+ "200":
664
+ description: Portfolio and per-contributor merged PR weekly series
665
+ content:
666
+ application/json:
667
+ schema:
668
+ $ref: "#/components/schemas/ProductivityInsightsMergedPrTrendsDto"
640
669
  "400":
641
- description: Body fails validation (missing/invalid required field, bad enum, conditional rule)
670
+ description: Invalid query parameters
642
671
  content:
643
672
  application/json:
644
673
  schema:
645
- $ref: "#/components/schemas/ErrorMessageDto"
674
+ type: object
675
+ required: [error]
676
+ properties:
677
+ error: { type: string, example: invalid_query }
646
678
  "401":
647
679
  $ref: "#/components/responses/Unauthorized"
648
680
  "403":
649
681
  $ref: "#/components/responses/Forbidden"
650
682
  "404":
651
- description: Workspace does not exist
652
- content:
653
- application/json:
654
- schema:
655
- $ref: "#/components/schemas/ErrorMessageDto"
683
+ description: Workspace does not exist or malformed workspaceId
656
684
 
657
- /workspaces/{workspaceId}/integrations/github:
685
+ /workspaces/{workspaceId}/insights/productivity-insights/teams:
658
686
  get:
659
- tags: [Integrations]
660
- summary: GitHub App installation status for workspace
661
- operationId: getWorkspaceGithubInstallationStatus
687
+ tags: [Workspace]
688
+ summary: Productivity insights top teams and team drilldown
662
689
  description: >
663
- Returns whether a GitHub App installation is linked and cached repo metadata.
664
- When no installation exists, returns 200 with `installed: false` (never 404 for missing install).
665
- Use `refresh=true` to bypass cache and re-list repositories from GitHub.
690
+ Without `teamId`: returns the top-ranked teams by average weekly merged PR count for the
691
+ Measure **Top teams** widget on /phase-c/c4 and /phase-c/c7. With `teamId`: returns weekly
692
+ rollup and summary for a single team (powers `/phase-c/c4/teams/:teamId` drilldown).
693
+ operationId: getProductivityInsightsTeams
666
694
  parameters:
667
695
  - $ref: "#/components/parameters/WorkspaceId"
668
- - name: refresh
696
+ - name: teamId
669
697
  in: query
670
- required: false
671
- description: When true, bypass `tai_workspace_vcs_repos_cache` and re-fetch from GitHub.
698
+ description: >
699
+ When set, return drilldown for this org-chart team id instead of the top-team list.
672
700
  schema:
673
- type: boolean
674
- default: false
701
+ type: string
702
+ - name: limit
703
+ in: query
704
+ description: Maximum top-team rows when `teamId` is omitted (UI default 3).
705
+ schema:
706
+ type: integer
707
+ minimum: 1
708
+ maximum: 10
709
+ default: 3
710
+ - name: rangeId
711
+ in: query
712
+ description: Measure time range (maps to weekly bucket count).
713
+ schema:
714
+ type: string
715
+ enum: [4w, 8w, q]
716
+ default: 4w
717
+ - $ref: "#/components/parameters/InsightsDepartmentFilter"
718
+ - name: team
719
+ in: query
720
+ description: Team name filter; send `All teams` to include every team.
721
+ schema:
722
+ type: string
723
+ default: All teams
724
+ - name: windowEnd
725
+ in: query
726
+ description: ISO timestamp for the end of the last weekly bucket; omit for current week.
727
+ schema:
728
+ type: string
729
+ format: date-time
675
730
  responses:
676
731
  "200":
677
- description: Installation status
732
+ description: Top teams list or single-team drilldown
678
733
  content:
679
734
  application/json:
680
735
  schema:
681
- $ref: "#/components/schemas/GithubInstallationStatusDto"
736
+ $ref: "#/components/schemas/ProductivityInsightsTeamsResponseDto"
737
+ "400":
738
+ description: Invalid query parameters
739
+ content:
740
+ application/json:
741
+ schema:
742
+ type: object
743
+ required: [error]
744
+ properties:
745
+ error: { type: string, example: invalid_query }
682
746
  "401":
683
747
  $ref: "#/components/responses/Unauthorized"
684
748
  "403":
685
749
  $ref: "#/components/responses/Forbidden"
686
750
  "404":
687
- description: Workspace does not exist or malformed workspaceId
688
- content:
689
- application/json:
690
- schema:
691
- $ref: "#/components/schemas/ErrorMessageDto"
751
+ description: Workspace or team not found
692
752
 
693
- delete:
694
- tags: [Integrations]
695
- summary: Disconnect GitHub App installation for workspace
696
- operationId: deleteWorkspaceGithubInstallation
753
+ /workspaces/{workspaceId}/insights/productivity-insights/contributors/{contributorId}:
754
+ get:
755
+ tags: [Workspace]
756
+ summary: Productivity insights — contributor drilldown
697
757
  description: >
698
- Removes the workspace GitHub App integration row (and cascaded VCS connection + repos cache).
699
- Idempotent: returns 204 when no installation exists. Does not uninstall the app on GitHub;
700
- use the manage URL or GitHub settings to revoke the app there if required.
701
- security:
702
- - bearerAuth: []
758
+ Weekly and daily activity rollups for a single contributor on
759
+ `/phase-c/c4/contributors/:id`. Distributes weekly merged PR counts from
760
+ precomputed person metrics across weekdays; includes fail-rate signals and
761
+ period-over-period summary deltas. Uses org-chart person ids (typically email).
762
+ operationId: getProductivityInsightsContributor
703
763
  parameters:
704
764
  - $ref: "#/components/parameters/WorkspaceId"
705
- - name: installationId
765
+ - name: contributorId
766
+ in: path
767
+ required: true
768
+ description: Org-chart person id (e.g. email from `person_id` external id).
769
+ schema:
770
+ type: string
771
+ - name: rangeId
706
772
  in: query
707
- required: false
708
- description: Optional installation id to disconnect a single linked installation.
773
+ description: Measure time range (maps to weekly bucket count).
709
774
  schema:
710
775
  type: string
711
- - name: all
776
+ enum: [4w, 8w, q]
777
+ default: 4w
778
+ - $ref: "#/components/parameters/InsightsDepartmentFilter"
779
+ - name: team
712
780
  in: query
713
- required: false
714
- description: When true, disconnect all linked GitHub installations for this workspace.
781
+ description: Team name filter; send `All teams` to include every team.
715
782
  schema:
716
- type: boolean
783
+ type: string
784
+ default: All teams
785
+ - name: windowEnd
786
+ in: query
787
+ description: ISO timestamp for the end of the last weekly bucket; omit for current week.
788
+ schema:
789
+ type: string
790
+ format: date-time
717
791
  responses:
718
- "204":
719
- description: Installation removed or was already absent
792
+ "200":
793
+ description: Contributor activity drilldown
794
+ content:
795
+ application/json:
796
+ schema:
797
+ $ref: "#/components/schemas/ProductivityInsightsContributorDetailDto"
798
+ "400":
799
+ description: Invalid query parameters
800
+ content:
801
+ application/json:
802
+ schema:
803
+ type: object
804
+ required: [error]
805
+ properties:
806
+ error: { type: string, example: invalid_query }
720
807
  "401":
721
808
  $ref: "#/components/responses/Unauthorized"
722
809
  "403":
723
810
  $ref: "#/components/responses/Forbidden"
724
811
  "404":
725
- description: Workspace does not exist or malformed workspaceId
726
- content:
727
- application/json:
728
- schema:
729
- $ref: "#/components/schemas/ErrorMessageDto"
812
+ description: Workspace or contributor not found
730
813
 
731
- /workspaces/{workspaceId}/integrations/github/install-url:
732
- post:
733
- tags: [Integrations]
734
- summary: Create GitHub App installation URL
735
- operationId: postGithubInstallUrl
814
+ /workspaces/{workspaceId}/insights/productivity-insights/needs-attention:
815
+ get:
816
+ tags: [Workspace]
817
+ summary: Productivity insights needs attention (bottom performers)
736
818
  description: >
737
- Creates a GitHub App installation URL scoped to the workspace and caller.
738
- Body is optional; when omitted, defaults to `/phase-a/p3` return path.
739
- security:
740
- - bearerAuth: []
819
+ Bottom performers for the Measure "Needs attention" widget on /phase-c/c4
820
+ (LeadersLaggards laggards column). Returns the lowest-ranked teams or contributors
821
+ by average weekly merged PR count for the selected filters and range.
822
+ operationId: getProductivityInsightsNeedsAttention
741
823
  parameters:
742
824
  - $ref: "#/components/parameters/WorkspaceId"
743
- requestBody:
744
- required: false
745
- content:
746
- application/json:
747
- schema:
748
- $ref: "#/components/schemas/GithubInstallUrlRequestDto"
825
+ - name: entityType
826
+ in: query
827
+ description: Rank teams (Aggregated Teams tab) or contributors (Individual Contributors tab).
828
+ schema:
829
+ type: string
830
+ enum: [teams, contributors]
831
+ default: contributors
832
+ - name: limit
833
+ in: query
834
+ description: Maximum number of laggard rows to return (UI default 3).
835
+ schema:
836
+ type: integer
837
+ minimum: 1
838
+ maximum: 10
839
+ default: 3
840
+ - name: rangeId
841
+ in: query
842
+ description: Measure time range (maps to weekly bucket count).
843
+ schema:
844
+ type: string
845
+ enum: [4w, 8w, q]
846
+ default: 4w
847
+ - $ref: "#/components/parameters/InsightsDepartmentFilter"
848
+ - name: team
849
+ in: query
850
+ description: Team name filter; send `All teams` to include every team.
851
+ schema:
852
+ type: string
853
+ default: All teams
854
+ - name: windowEnd
855
+ in: query
856
+ description: ISO timestamp for the end of the last weekly bucket; omit for current week.
857
+ schema:
858
+ type: string
859
+ format: date-time
749
860
  responses:
750
861
  "200":
751
- description: Installation URL + signed state
862
+ description: Bottom performers for the needs-attention widget
752
863
  content:
753
864
  application/json:
754
865
  schema:
755
- $ref: "#/components/schemas/GithubInstallUrlResponseDto"
866
+ $ref: "#/components/schemas/ProductivityInsightsNeedsAttentionDto"
756
867
  "400":
757
- description: Request validation failed
868
+ description: Invalid query parameters
758
869
  content:
759
870
  application/json:
760
871
  schema:
761
- $ref: "#/components/schemas/ErrorMessageDto"
872
+ type: object
873
+ required: [error]
874
+ properties:
875
+ error: { type: string, example: invalid_query }
762
876
  "401":
763
877
  $ref: "#/components/responses/Unauthorized"
764
878
  "403":
765
879
  $ref: "#/components/responses/Forbidden"
766
880
  "404":
767
881
  description: Workspace does not exist or malformed workspaceId
768
- content:
769
- application/json:
770
- schema:
771
- $ref: "#/components/schemas/ErrorMessageDto"
772
882
 
773
- /workspaces/{workspaceId}/integrations/ai-tool/{provider}/refresh:
774
- post:
775
- tags: [Integrations]
776
- summary: Refresh AI tool integration snapshots
777
- operationId: refreshWorkspaceAiTool
883
+ /workspaces/{workspaceId}/insights/ai-usage-spend/aggregated-teams:
884
+ get:
885
+ tags: [Workspace]
886
+ summary: AI usage & spend insights — aggregated team weekly rollups
778
887
  description: >
779
- Provider-agnostic refresh. Returns 409 when an advisory-lock refresh is already in progress.
780
- On upstream auth/rate/network failures, returns `200` with `{ ok:false, error }` so the SPA can render inline error.
888
+ Team-level AI adoption, token volume, and spend for the Measure **Aggregated Teams**
889
+ view on `/phase-c/c7` and the dashboard AI Usage & Spend card. Aggregates stored
890
+ AI-tool integration snapshots (daily usage + billing spend) by org-chart team using
891
+ member email. Same filter params as other Measure insights routes.
892
+ operationId: getAiUsageSpendAggregatedTeams
781
893
  parameters:
782
894
  - $ref: "#/components/parameters/WorkspaceId"
783
- - name: provider
784
- in: path
785
- required: true
895
+ - name: rangeId
896
+ in: query
897
+ description: Measure time range (maps to weekly bucket count).
786
898
  schema:
787
899
  type: string
788
- enum: [cursor]
900
+ enum: [4w, 8w, q]
901
+ default: 4w
902
+ - $ref: "#/components/parameters/InsightsDepartmentFilter"
903
+ - name: team
904
+ in: query
905
+ description: Team name filter; send `All teams` to include every team.
906
+ schema:
907
+ type: string
908
+ default: All teams
909
+ - name: windowEnd
910
+ in: query
911
+ description: ISO timestamp for the end of the last weekly bucket; omit for current week.
912
+ schema:
913
+ type: string
914
+ format: date-time
789
915
  responses:
790
916
  "200":
791
- description: Refresh result
917
+ description: Aggregated team AI usage and spend series
792
918
  content:
793
919
  application/json:
794
920
  schema:
795
- $ref: "#/components/schemas/AiToolRefreshResponseDto"
796
- examples:
797
- ok:
798
- value: { ok: true }
799
- invalid_token:
800
- value: { ok: false, error: invalid_token }
921
+ $ref: "#/components/schemas/AiUsageSpendAggregatedTeamsDto"
922
+ "400":
923
+ description: Invalid query parameters
924
+ content:
925
+ application/json:
926
+ schema:
927
+ type: object
928
+ required: [error]
929
+ properties:
930
+ error: { type: string, example: invalid_query }
801
931
  "401":
802
932
  $ref: "#/components/responses/Unauthorized"
803
933
  "403":
804
934
  $ref: "#/components/responses/Forbidden"
805
935
  "404":
806
- description: Provider source missing for this workspace
807
- content:
808
- application/json:
809
- schema:
810
- $ref: "#/components/schemas/AiToolRefreshResponseDto"
811
- example:
812
- ok: false
813
- error: no_source
814
- "409":
815
- description: Refresh already in progress
816
- content:
817
- application/json:
818
- schema:
819
- $ref: "#/components/schemas/AiToolRefreshResponseDto"
820
- example:
821
- ok: false
822
- error: already_in_progress
936
+ description: Workspace does not exist or malformed workspaceId
823
937
 
824
- /workspaces/{workspaceId}/integrations/ai-tool/{provider}/seats:
938
+ /workspaces/{workspaceId}/insights/ai-usage-spend/aggregated-contributors:
825
939
  get:
826
- tags: [Integrations]
827
- summary: List latest AI-tool seats
828
- operationId: getWorkspaceAiToolSeats
940
+ tags: [Workspace]
941
+ summary: AI usage & spend insights — aggregated contributor weekly rollups
942
+ description: >
943
+ Per-contributor AI adoption, token volume, and spend for the Measure **Individual
944
+ Contributors** view on `/phase-c/c7`. Aggregates stored AI-tool integration snapshots
945
+ (daily usage + billing spend) by org-chart person id (typically email). Same filter
946
+ params as other Measure insights routes.
947
+ operationId: getAiUsageSpendAggregatedContributors
829
948
  parameters:
830
949
  - $ref: "#/components/parameters/WorkspaceId"
831
- - name: provider
832
- in: path
833
- required: true
950
+ - name: rangeId
951
+ in: query
952
+ description: Measure time range (maps to weekly bucket count).
834
953
  schema:
835
954
  type: string
836
- enum: [cursor]
955
+ enum: [4w, 8w, q]
956
+ default: 4w
957
+ - $ref: "#/components/parameters/InsightsDepartmentFilter"
958
+ - name: team
959
+ in: query
960
+ description: Team name filter; send `All teams` to include every team.
961
+ schema:
962
+ type: string
963
+ default: All teams
964
+ - name: windowEnd
965
+ in: query
966
+ description: ISO timestamp for the end of the last weekly bucket; omit for current week.
967
+ schema:
968
+ type: string
969
+ format: date-time
837
970
  responses:
838
971
  "200":
839
- description: Seats
972
+ description: Aggregated contributor AI usage and spend series
973
+ content:
974
+ application/json:
975
+ schema:
976
+ $ref: "#/components/schemas/AiUsageSpendAggregatedContributorsDto"
977
+ "400":
978
+ description: Invalid query parameters
840
979
  content:
841
980
  application/json:
842
981
  schema:
843
982
  type: object
844
- required: [seats]
983
+ required: [error]
845
984
  properties:
846
- seats:
847
- type: array
848
- items:
849
- $ref: "#/components/schemas/AiToolSeatDto"
985
+ error: { type: string, example: invalid_query }
850
986
  "401":
851
987
  $ref: "#/components/responses/Unauthorized"
852
988
  "403":
853
989
  $ref: "#/components/responses/Forbidden"
990
+ "404":
991
+ description: Workspace does not exist or malformed workspaceId
854
992
 
855
- /workspaces/{workspaceId}/integrations/ai-tool/{provider}/daily-usage:
993
+ /workspaces/{workspaceId}/insights/ai-usage-spend/contributor-tool-heatmap:
856
994
  get:
857
- tags: [Integrations]
858
- summary: List AI-tool daily usage
859
- operationId: getWorkspaceAiToolDailyUsage
995
+ tags: [Workspace]
996
+ summary: AI usage & spend insights — contributor × week heatmap rows
997
+ description: >
998
+ Contributor × week heatmap rows for the Measure **Individual Contributors** tool
999
+ heatmap on `/phase-c/c7`. Same aggregation as aggregated-contributors; returns
1000
+ weekly token, spend, and active-user counts per org-chart person.
1001
+ operationId: getAiUsageSpendContributorToolHeatmap
860
1002
  parameters:
861
1003
  - $ref: "#/components/parameters/WorkspaceId"
862
- - name: provider
863
- in: path
864
- required: true
865
- schema:
866
- type: string
867
- enum: [cursor]
868
- - name: from
1004
+ - name: rangeId
869
1005
  in: query
870
- required: false
1006
+ description: Measure time range (maps to weekly bucket count).
871
1007
  schema:
872
1008
  type: string
873
- format: date
874
- - name: to
1009
+ enum: [4w, 8w, q]
1010
+ default: 4w
1011
+ - $ref: "#/components/parameters/InsightsDepartmentFilter"
1012
+ - name: team
875
1013
  in: query
876
- required: false
1014
+ description: Team name filter; send `All teams` to include every team.
877
1015
  schema:
878
1016
  type: string
879
- format: date
880
- - name: externalId
1017
+ default: All teams
1018
+ - name: windowEnd
881
1019
  in: query
882
- required: false
1020
+ description: ISO timestamp for the end of the last weekly bucket; omit for current week.
883
1021
  schema:
884
1022
  type: string
1023
+ format: date-time
885
1024
  responses:
886
1025
  "200":
887
- description: Usage rows
1026
+ description: Contributor heatmap rows for AI usage and spend
1027
+ content:
1028
+ application/json:
1029
+ schema:
1030
+ $ref: "#/components/schemas/AiUsageSpendContributorToolHeatmapDto"
1031
+ "400":
1032
+ description: Invalid query parameters
888
1033
  content:
889
1034
  application/json:
890
1035
  schema:
891
1036
  type: object
892
- required: [rows]
1037
+ required: [error]
893
1038
  properties:
894
- rows:
895
- type: array
896
- items:
897
- $ref: "#/components/schemas/AiToolDailyUsageRowDto"
1039
+ error: { type: string, example: invalid_query }
898
1040
  "401":
899
1041
  $ref: "#/components/responses/Unauthorized"
900
1042
  "403":
901
1043
  $ref: "#/components/responses/Forbidden"
1044
+ "404":
1045
+ description: Workspace does not exist or malformed workspaceId
902
1046
 
903
- /workspaces/{workspaceId}/integrations/ai-tool/{provider}/spend:
1047
+ /workspaces/{workspaceId}/insights/ai-usage-spend/key-insights:
904
1048
  get:
905
- tags: [Integrations]
906
- summary: List AI-tool spend
907
- operationId: getWorkspaceAiToolSpend
1049
+ tags: [Workspace]
1050
+ summary: AI usage & spend insights — key insights panel
1051
+ description: >
1052
+ Narrative insight cards for the Measure **Key insights** panel on `/phase-c/c7`
1053
+ (AI Usage & Spend). Derived from scoped AI-tool usage/spend snapshots and
1054
+ productivity metrics for the selected filters and range.
1055
+ operationId: getAiUsageSpendKeyInsights
908
1056
  parameters:
909
1057
  - $ref: "#/components/parameters/WorkspaceId"
910
- - name: provider
911
- in: path
912
- required: true
1058
+ - name: rangeId
1059
+ in: query
1060
+ description: Measure time range (maps to weekly bucket count).
913
1061
  schema:
914
1062
  type: string
915
- enum: [cursor]
916
- - name: cycleStartMs
1063
+ enum: [4w, 8w, q]
1064
+ default: 4w
1065
+ - $ref: "#/components/parameters/InsightsDepartmentFilter"
1066
+ - name: team
917
1067
  in: query
918
- required: false
1068
+ description: Team name filter; send `All teams` to include every team.
919
1069
  schema:
920
- type: integer
921
- format: int64
1070
+ type: string
1071
+ default: All teams
1072
+ - name: windowEnd
1073
+ in: query
1074
+ description: ISO timestamp for the end of the last weekly bucket; omit for current week.
1075
+ schema:
1076
+ type: string
1077
+ format: date-time
922
1078
  responses:
923
1079
  "200":
924
- description: Spend rows
1080
+ description: Key insight cards for the AI usage & spend panel
1081
+ content:
1082
+ application/json:
1083
+ schema:
1084
+ $ref: "#/components/schemas/AiUsageSpendKeyInsightsDto"
1085
+ "400":
1086
+ description: Invalid query parameters
925
1087
  content:
926
1088
  application/json:
927
1089
  schema:
928
1090
  type: object
929
- required: [rows]
1091
+ required: [error]
930
1092
  properties:
931
- rows:
932
- type: array
933
- items:
934
- $ref: "#/components/schemas/AiToolSpendRowDto"
1093
+ error: { type: string, example: invalid_query }
935
1094
  "401":
936
1095
  $ref: "#/components/responses/Unauthorized"
937
1096
  "403":
938
1097
  $ref: "#/components/responses/Forbidden"
1098
+ "404":
1099
+ description: Workspace does not exist or malformed workspaceId
939
1100
 
940
- /workspaces/{workspaceId}/integrations/ai-tool/{provider}/status:
1101
+ /workspaces/{workspaceId}/insights/ai-usage-spend/ai-assisted-code-percentage:
941
1102
  get:
942
- tags: [Integrations]
943
- summary: Get last refresh status
944
- operationId: getWorkspaceAiToolRefreshStatus
1103
+ tags: [Workspace]
1104
+ summary: AI usage & spend insights — AI-assisted code percentage
1105
+ description: >
1106
+ Period and weekly AI-assisted code percentage for the Measure **AI-assisted code**
1107
+ card on `/phase-c/c7` and the **AI impact** curve on `/phase-c/c4`. When scoped
1108
+ productivity metrics include `aiAssistedContributionRatioPercent`, values are
1109
+ **measured** from ingest; otherwise the API falls back to token→LOC heuristics and
1110
+ sets `dataQuality` to **estimated**. Clients should not present estimated series as
1111
+ Cursor-measured signal.
1112
+ operationId: getAiUsageSpendAiAssistedCodePercentage
945
1113
  parameters:
946
1114
  - $ref: "#/components/parameters/WorkspaceId"
947
- - name: provider
948
- in: path
949
- required: true
1115
+ - name: rangeId
1116
+ in: query
1117
+ description: Measure time range (maps to weekly bucket count).
950
1118
  schema:
951
1119
  type: string
952
- enum: [cursor]
1120
+ enum: [4w, 8w, q]
1121
+ default: 4w
1122
+ - $ref: "#/components/parameters/InsightsDepartmentFilter"
1123
+ - name: team
1124
+ in: query
1125
+ description: Team name filter; send `All teams` to include every team.
1126
+ schema:
1127
+ type: string
1128
+ default: All teams
1129
+ - name: windowEnd
1130
+ in: query
1131
+ description: ISO timestamp for the end of the last weekly bucket; omit for current week.
1132
+ schema:
1133
+ type: string
1134
+ format: date-time
953
1135
  responses:
954
1136
  "200":
955
- description: Refresh status
1137
+ description: AI-assisted code percentage series
1138
+ content:
1139
+ application/json:
1140
+ schema:
1141
+ $ref: "#/components/schemas/AiUsageSpendAiAssistedCodePercentageDto"
1142
+ "400":
1143
+ description: Invalid query parameters
956
1144
  content:
957
1145
  application/json:
958
1146
  schema:
959
1147
  type: object
960
- required: [status]
1148
+ required: [error]
961
1149
  properties:
962
- status:
963
- $ref: "#/components/schemas/AiToolRefreshStatusDto"
1150
+ error: { type: string, example: invalid_query }
964
1151
  "401":
965
1152
  $ref: "#/components/responses/Unauthorized"
966
1153
  "403":
967
1154
  $ref: "#/components/responses/Forbidden"
968
1155
  "404":
969
- description: No refresh status yet (or provider source missing)
970
- content:
971
- application/json:
972
- schema:
973
- $ref: "#/components/schemas/AiToolRefreshResponseDto"
974
- example:
975
- ok: false
976
- error: no_source
1156
+ description: Workspace does not exist or malformed workspaceId
977
1157
 
978
- /integrations/github/callback:
1158
+ /workspaces/{workspaceId}/insights/ai-usage-spend/ai-tool-usage-spend:
979
1159
  get:
980
- tags: [Integrations]
981
- summary: GitHub App installation callback
982
- operationId: getGithubInstallCallback
1160
+ tags: [Workspace]
1161
+ summary: AI usage & spend insights — AI tool usage and spend by provider/model
983
1162
  description: >
984
- Public callback endpoint used by GitHub App installation flow.
985
- Resolves and consumes install state, persists workspace installation linkage,
986
- then redirects to configured SPA origin with status markers.
1163
+ Scoped portfolio rollups of AI-tool token usage and spend by registered provider
1164
+ and by model (from daily usage `mostUsedModel`) for the **Provider / Model usage + spend**
1165
+ chart on `/phase-c/c4` and `/phase-c/c7`. Reads stored AI-tool integration snapshots
1166
+ (`tai_workspace_ai_tool_daily_usage`, `tai_workspace_ai_tool_spend`).
1167
+ operationId: getAiUsageSpendAiToolUsageSpend
987
1168
  parameters:
988
- - name: state
989
- in: query
990
- required: true
991
- schema:
992
- type: string
993
- - name: installation_id
994
- in: query
995
- required: false
996
- schema:
997
- type: string
998
- - name: setup_action
1169
+ - $ref: "#/components/parameters/WorkspaceId"
1170
+ - name: rangeId
999
1171
  in: query
1000
- required: false
1172
+ description: Measure time range (maps to weekly bucket count).
1001
1173
  schema:
1002
1174
  type: string
1003
- - name: error
1175
+ enum: [4w, 8w, q]
1176
+ default: 4w
1177
+ - $ref: "#/components/parameters/InsightsDepartmentFilter"
1178
+ - name: team
1004
1179
  in: query
1005
- required: false
1180
+ description: Team name filter; send `All teams` to include every team.
1006
1181
  schema:
1007
1182
  type: string
1008
- - name: error_description
1183
+ default: All teams
1184
+ - name: windowEnd
1009
1185
  in: query
1010
- required: false
1186
+ description: ISO timestamp for the end of the last weekly bucket; omit for current week.
1011
1187
  schema:
1012
1188
  type: string
1189
+ format: date-time
1013
1190
  responses:
1014
- "302":
1015
- description: Redirect to ThinkAI SPA return path with GitHub connection status
1016
- "501":
1017
- description: Workspace store not configured
1191
+ "200":
1192
+ description: Provider and model AI tool usage/spend rollups
1018
1193
  content:
1019
1194
  application/json:
1020
1195
  schema:
1021
- $ref: "#/components/schemas/ErrorMessageDto"
1022
- "503":
1023
- description: SPA origin configuration missing
1196
+ $ref: "#/components/schemas/AiUsageSpendAiToolUsageSpendDto"
1197
+ "400":
1198
+ description: Invalid query parameters
1024
1199
  content:
1025
1200
  application/json:
1026
1201
  schema:
1027
- $ref: "#/components/schemas/ErrorMessageDto"
1202
+ type: object
1203
+ required: [error]
1204
+ properties:
1205
+ error: { type: string, example: invalid_query }
1206
+ "401":
1207
+ $ref: "#/components/responses/Unauthorized"
1208
+ "403":
1209
+ $ref: "#/components/responses/Forbidden"
1210
+ "404":
1211
+ description: Workspace does not exist or malformed workspaceId
1028
1212
 
1029
- /integrations/github/webhook:
1030
- post:
1031
- tags: [Integrations]
1032
- summary: GitHub App webhook receiver
1033
- operationId: postGithubWebhook
1213
+ /workspaces/{workspaceId}/insights/ai-usage-spend/team-tool-heatmap:
1214
+ get:
1215
+ tags: [Workspace]
1216
+ summary: AI usage & spend insights — team weekly heatmap
1034
1217
  description: >
1035
- Public webhook endpoint that verifies `X-Hub-Signature-256` and updates or
1036
- removes workspace GitHub integration/cache state for installation events.
1218
+ Team × week rollups of AI-tool token volume, spend, and active users for the
1219
+ **Team tool heatmap** on `/phase-c/c4` and `/phase-c/c7`. Aggregates stored
1220
+ AI-tool integration snapshots by org-chart team. Same filter params as other
1221
+ Measure insights routes.
1222
+ operationId: getAiUsageSpendTeamToolHeatmap
1037
1223
  parameters:
1038
- - name: X-Hub-Signature-256
1039
- in: header
1040
- required: true
1224
+ - $ref: "#/components/parameters/WorkspaceId"
1225
+ - name: rangeId
1226
+ in: query
1227
+ description: Measure time range (maps to weekly bucket count).
1041
1228
  schema:
1042
1229
  type: string
1043
- - name: X-GitHub-Event
1044
- in: header
1045
- required: true
1230
+ enum: [4w, 8w, q]
1231
+ default: 4w
1232
+ - $ref: "#/components/parameters/InsightsDepartmentFilter"
1233
+ - name: team
1234
+ in: query
1235
+ description: Team name filter; send `All teams` to include every team.
1046
1236
  schema:
1047
1237
  type: string
1048
- requestBody:
1049
- required: true
1050
- content:
1051
- application/json:
1052
- schema:
1053
- type: object
1054
- additionalProperties: true
1238
+ default: All teams
1239
+ - name: windowEnd
1240
+ in: query
1241
+ description: ISO timestamp for the end of the last weekly bucket; omit for current week.
1242
+ schema:
1243
+ type: string
1244
+ format: date-time
1055
1245
  responses:
1056
1246
  "200":
1057
- description: Webhook accepted
1058
- "400":
1059
- description: Invalid payload
1247
+ description: Team weekly AI usage heatmap rows
1060
1248
  content:
1061
1249
  application/json:
1062
1250
  schema:
1063
- $ref: "#/components/schemas/ErrorMessageDto"
1064
- "401":
1065
- description: Invalid webhook signature
1251
+ $ref: "#/components/schemas/AiUsageSpendTeamToolHeatmapDto"
1252
+ "400":
1253
+ description: Invalid query parameters
1066
1254
  content:
1067
1255
  application/json:
1068
1256
  schema:
1069
- $ref: "#/components/schemas/ErrorMessageDto"
1070
- "501":
1071
- description: Workspace store not configured
1072
- content:
1073
- application/json:
1074
- schema:
1075
- $ref: "#/components/schemas/ErrorMessageDto"
1076
- "503":
1077
- description: Missing GitHub webhook secret or GitHub App configuration
1078
- content:
1079
- application/json:
1080
- schema:
1081
- $ref: "#/components/schemas/ErrorMessageDto"
1257
+ type: object
1258
+ required: [error]
1259
+ properties:
1260
+ error: { type: string, example: invalid_query }
1261
+ "401":
1262
+ $ref: "#/components/responses/Unauthorized"
1263
+ "403":
1264
+ $ref: "#/components/responses/Forbidden"
1265
+ "404":
1266
+ description: Workspace does not exist or malformed workspaceId
1082
1267
 
1083
- /workspaces/{workspaceId}/ai-readiness:
1268
+ /workspaces/{workspaceId}/insights/ai-usage-spend/roi-productivity-signal:
1084
1269
  get:
1085
- tags: [AIReadiness]
1086
- summary: Get AI readiness
1087
- operationId: getAiReadiness
1270
+ tags: [Workspace]
1271
+ summary: AI usage & spend insights — ROI and productivity signal
1272
+ description: >
1273
+ Connects scoped AI-tool usage and spend to engineering throughput (merged PRs)
1274
+ for the **ROI & Productivity Signal** panel on `/phase-c/c4` and `/phase-c/c7`.
1275
+ Returns spend efficiency metrics, adoption counts, and weekly usage vs PR series.
1276
+ Same filter params as other Measure insights routes.
1277
+ operationId: getAiUsageSpendRoiProductivitySignal
1088
1278
  parameters:
1089
1279
  - $ref: "#/components/parameters/WorkspaceId"
1280
+ - name: rangeId
1281
+ in: query
1282
+ description: Measure time range (maps to weekly bucket count).
1283
+ schema:
1284
+ type: string
1285
+ enum: [4w, 8w, q]
1286
+ default: 4w
1287
+ - $ref: "#/components/parameters/InsightsDepartmentFilter"
1288
+ - name: team
1289
+ in: query
1290
+ description: Team name filter; send `All teams` to include every team.
1291
+ schema:
1292
+ type: string
1293
+ default: All teams
1294
+ - name: windowEnd
1295
+ in: query
1296
+ description: ISO timestamp for the end of the last weekly bucket; omit for current week.
1297
+ schema:
1298
+ type: string
1299
+ format: date-time
1090
1300
  responses:
1091
1301
  "200":
1092
- description: State or JSON null
1302
+ description: ROI and productivity signal rollups
1093
1303
  content:
1094
1304
  application/json:
1095
1305
  schema:
1096
- $ref: "#/components/schemas/AIReadinessDto"
1097
- "401":
1098
- $ref: "#/components/responses/Unauthorized"
1099
- put:
1100
- tags: [AIReadiness]
1101
- summary: Upsert AI readiness
1102
- operationId: putAiReadiness
1103
- parameters:
1104
- - $ref: "#/components/parameters/WorkspaceId"
1105
- requestBody:
1106
- required: true
1107
- content:
1108
- application/json:
1109
- schema:
1110
- $ref: "#/components/schemas/AIReadinessDto"
1111
- responses:
1112
- "204":
1113
- description: Saved
1114
- "401":
1115
- $ref: "#/components/responses/Unauthorized"
1116
-
1117
- /workspaces/{workspaceId}/ai-readiness/maturity:
1118
- post:
1119
- tags: [AIReadiness]
1120
- summary: Compute maturity level
1121
- operationId: postAiReadinessMaturity
1122
- parameters:
1123
- - $ref: "#/components/parameters/WorkspaceId"
1124
- requestBody:
1125
- required: true
1126
- content:
1127
- application/json:
1128
- schema:
1129
- $ref: "#/components/schemas/AIReadinessDto"
1130
- responses:
1131
- "200":
1132
- description: Derived level
1306
+ $ref: "#/components/schemas/AiUsageSpendRoiProductivitySignalDto"
1307
+ "400":
1308
+ description: Invalid query parameters
1133
1309
  content:
1134
1310
  application/json:
1135
1311
  schema:
1136
- $ref: "#/components/schemas/AiMaturityResultDto"
1312
+ type: object
1313
+ required: [error]
1314
+ properties:
1315
+ error: { type: string, example: invalid_query }
1137
1316
  "401":
1138
1317
  $ref: "#/components/responses/Unauthorized"
1318
+ "403":
1319
+ $ref: "#/components/responses/Forbidden"
1320
+ "404":
1321
+ description: Workspace does not exist or malformed workspaceId
1139
1322
 
1140
- /workspaces/{workspaceId}/tech-stack:
1323
+ /workspaces/{workspaceId}/company-profile:
1141
1324
  get:
1142
- tags: [TechStack]
1143
- summary: Get tech stack
1144
- operationId: getTechStack
1325
+ tags: [CompanyProfile]
1326
+ summary: Get company profile
1327
+ operationId: getCompanyProfile
1145
1328
  parameters:
1146
1329
  - $ref: "#/components/parameters/WorkspaceId"
1147
1330
  responses:
1148
1331
  "200":
1149
1332
  description: >
1150
- Saved tech stack DTO, or JSON `null` when the workspace exists but no stack
1151
- has been saved yet. Clients should merge `null` with their empty defaults.
1333
+ Company profile DTO, or JSON `null` when the workspace exists but no profile
1334
+ has been saved yet. Clients should treat `null` as the empty initial state
1335
+ (the wizard renders blank inputs); they MUST NOT treat the absence of a
1336
+ saved profile as a 404.
1152
1337
  content:
1153
1338
  application/json:
1154
1339
  schema:
1155
- nullable: true
1156
- allOf:
1157
- - $ref: "#/components/schemas/TechStackDto"
1340
+ $ref: "#/components/schemas/CompanyProfileDto"
1341
+ examples:
1342
+ sample:
1343
+ summary: Saved profile
1344
+ value:
1345
+ companyName: Acme
1346
+ industry: software
1347
+ companySize: "51-200"
1348
+ engineeringHC: "42"
1349
+ headquarters: "Austin, TX"
1158
1350
  "400":
1159
1351
  description: Malformed workspaceId
1160
1352
  content:
@@ -1172,9 +1364,14 @@ paths:
1172
1364
  schema:
1173
1365
  $ref: "#/components/schemas/ErrorMessageDto"
1174
1366
  put:
1175
- tags: [TechStack]
1176
- summary: Upsert tech stack
1177
- operationId: putTechStack
1367
+ tags: [CompanyProfile]
1368
+ summary: Upsert company profile
1369
+ operationId: putCompanyProfile
1370
+ description: >
1371
+ Idempotent upsert of the workspace's company profile. On success, also
1372
+ synchronizes `tai_workspaces.name` to the new `companyName` so subsequent
1373
+ calls to `GET /me/workspaces` reflect the updated display name. Repeated
1374
+ PUTs replace the stored payload entirely.
1178
1375
  parameters:
1179
1376
  - $ref: "#/components/parameters/WorkspaceId"
1180
1377
  requestBody:
@@ -1182,12 +1379,12 @@ paths:
1182
1379
  content:
1183
1380
  application/json:
1184
1381
  schema:
1185
- $ref: "#/components/schemas/TechStackDto"
1382
+ $ref: "#/components/schemas/CompanyProfileDto"
1186
1383
  responses:
1187
1384
  "204":
1188
1385
  description: Saved
1189
1386
  "400":
1190
- description: Body fails validation (unknown keys, bounds, missing required fields)
1387
+ description: Body fails validation (missing/invalid required field, bad enum, conditional rule)
1191
1388
  content:
1192
1389
  application/json:
1193
1390
  schema:
@@ -1203,406 +1400,522 @@ paths:
1203
1400
  schema:
1204
1401
  $ref: "#/components/schemas/ErrorMessageDto"
1205
1402
 
1206
- /workspaces/{workspaceId}/nda:
1403
+ /workspaces/{workspaceId}/integrations/github:
1207
1404
  get:
1208
- tags: [NDA]
1209
- summary: Get NDA completion state
1210
- operationId: getNda
1405
+ tags: [Integrations]
1406
+ summary: GitHub App installation status for workspace
1407
+ operationId: getWorkspaceGithubInstallationStatus
1408
+ description: >
1409
+ Returns whether a GitHub App installation is linked and cached repo metadata.
1410
+ When no installation exists, returns 200 with `installed: false` (never 404 for missing install).
1411
+ Use `refresh=true` to bypass cache and re-list repositories from GitHub.
1211
1412
  parameters:
1212
1413
  - $ref: "#/components/parameters/WorkspaceId"
1414
+ - name: refresh
1415
+ in: query
1416
+ required: false
1417
+ description: When true, bypass `tai_workspace_vcs_repos_cache` and re-fetch from GitHub.
1418
+ schema:
1419
+ type: boolean
1420
+ default: false
1421
+ - name: permissionsRefresh
1422
+ in: query
1423
+ required: false
1424
+ description: >
1425
+ When true, re-fetch GitHub App and installation permission metadata (used by the
1426
+ login permission gate after the user approves on GitHub).
1427
+ schema:
1428
+ type: boolean
1429
+ default: false
1213
1430
  responses:
1214
1431
  "200":
1215
- description: Completion or JSON null
1432
+ description: Installation status
1216
1433
  content:
1217
1434
  application/json:
1218
1435
  schema:
1219
- $ref: "#/components/schemas/NdaCompletionDto"
1436
+ $ref: "#/components/schemas/GithubInstallationStatusDto"
1220
1437
  "401":
1221
1438
  $ref: "#/components/responses/Unauthorized"
1439
+ "403":
1440
+ $ref: "#/components/responses/Forbidden"
1441
+ "404":
1442
+ description: Workspace does not exist or malformed workspaceId
1443
+ content:
1444
+ application/json:
1445
+ schema:
1446
+ $ref: "#/components/schemas/ErrorMessageDto"
1222
1447
 
1223
- /workspaces/{workspaceId}/nda/template:
1224
- get:
1225
- tags: [NDA]
1226
- summary: NDA template for display
1227
- operationId: getNdaTemplate
1448
+ delete:
1449
+ tags: [Integrations]
1450
+ summary: Disconnect GitHub App installation for workspace
1451
+ operationId: deleteWorkspaceGithubInstallation
1452
+ description: >
1453
+ Removes the workspace GitHub App integration row (and cascaded VCS connection + repos cache).
1454
+ Idempotent: returns 204 when no installation exists. Does not uninstall the app on GitHub;
1455
+ use the manage URL or GitHub settings to revoke the app there if required.
1456
+ security:
1457
+ - bearerAuth: []
1228
1458
  parameters:
1229
1459
  - $ref: "#/components/parameters/WorkspaceId"
1460
+ - name: installationId
1461
+ in: query
1462
+ required: false
1463
+ description: Optional installation id to disconnect a single linked installation.
1464
+ schema:
1465
+ type: string
1466
+ - name: all
1467
+ in: query
1468
+ required: false
1469
+ description: When true, disconnect all linked GitHub installations for this workspace.
1470
+ schema:
1471
+ type: boolean
1230
1472
  responses:
1231
- "200":
1232
- description: Template
1473
+ "204":
1474
+ description: Installation removed or was already absent
1475
+ "401":
1476
+ $ref: "#/components/responses/Unauthorized"
1477
+ "403":
1478
+ $ref: "#/components/responses/Forbidden"
1479
+ "404":
1480
+ description: Workspace does not exist or malformed workspaceId
1233
1481
  content:
1234
1482
  application/json:
1235
1483
  schema:
1236
- $ref: "#/components/schemas/NdaTemplateDto"
1237
- "401":
1238
- $ref: "#/components/responses/Unauthorized"
1484
+ $ref: "#/components/schemas/ErrorMessageDto"
1239
1485
 
1240
- /workspaces/{workspaceId}/nda/sign:
1486
+ /workspaces/{workspaceId}/integrations/github/install-url:
1241
1487
  post:
1242
- tags: [NDA]
1243
- summary: Electronic NDA signature
1244
- operationId: postNdaSign
1488
+ tags: [Integrations]
1489
+ summary: Create GitHub App installation URL
1490
+ operationId: postGithubInstallUrl
1491
+ description: >
1492
+ Creates a GitHub App installation URL scoped to the workspace and caller.
1493
+ Body is optional; when omitted, defaults to `/phase-a/p3` return path.
1494
+ security:
1495
+ - bearerAuth: []
1245
1496
  parameters:
1246
1497
  - $ref: "#/components/parameters/WorkspaceId"
1247
1498
  requestBody:
1248
- required: true
1499
+ required: false
1249
1500
  content:
1250
1501
  application/json:
1251
1502
  schema:
1252
- $ref: "#/components/schemas/NdaSignatureDto"
1503
+ $ref: "#/components/schemas/GithubInstallUrlRequestDto"
1253
1504
  responses:
1254
1505
  "200":
1255
- description: Completion record
1506
+ description: Installation URL + signed state
1256
1507
  content:
1257
1508
  application/json:
1258
1509
  schema:
1259
- $ref: "#/components/schemas/NdaCompletionDto"
1510
+ $ref: "#/components/schemas/GithubInstallUrlResponseDto"
1511
+ "400":
1512
+ description: Request validation failed
1513
+ content:
1514
+ application/json:
1515
+ schema:
1516
+ $ref: "#/components/schemas/ErrorMessageDto"
1260
1517
  "401":
1261
1518
  $ref: "#/components/responses/Unauthorized"
1519
+ "403":
1520
+ $ref: "#/components/responses/Forbidden"
1521
+ "404":
1522
+ description: Workspace does not exist or malformed workspaceId
1523
+ content:
1524
+ application/json:
1525
+ schema:
1526
+ $ref: "#/components/schemas/ErrorMessageDto"
1262
1527
 
1263
- /workspaces/{workspaceId}/nda/external:
1528
+ /workspaces/{workspaceId}/integrations/ai-tool/{provider}/refresh:
1264
1529
  post:
1265
- tags: [NDA]
1266
- summary: External NDA path (after Supabase upload)
1530
+ tags: [Integrations]
1531
+ summary: Refresh AI tool integration snapshots
1532
+ operationId: refreshWorkspaceAiTool
1267
1533
  description: >
1268
- Preferred body uses `storagePath` after client upload to the `nda-documents` bucket (§14).
1269
- Multipart variant may be supported by the server as an alternative.
1270
- operationId: postNdaExternal
1271
- parameters:
1272
- - $ref: "#/components/parameters/WorkspaceId"
1273
- requestBody:
1274
- required: true
1275
- content:
1276
- application/json:
1277
- schema:
1278
- $ref: "#/components/schemas/NdaExternalBodyDto"
1279
- responses:
1280
- "200":
1281
- description: Completion record
1282
- content:
1283
- application/json:
1284
- schema:
1285
- $ref: "#/components/schemas/NdaCompletionDto"
1286
- "401":
1287
- $ref: "#/components/responses/Unauthorized"
1288
-
1289
- /workspaces/{workspaceId}/readiness/repos:
1290
- get:
1291
- tags: [RepositoryReadiness]
1292
- summary: List repo readiness scores
1293
- operationId: listReadinessRepos
1534
+ Provider-agnostic refresh. Returns 409 when an advisory-lock refresh is already in progress.
1535
+ On upstream auth/rate/network failures, returns `200` with `{ ok:false, error }` so the SPA can render inline error.
1294
1536
  parameters:
1295
1537
  - $ref: "#/components/parameters/WorkspaceId"
1538
+ - name: provider
1539
+ in: path
1540
+ required: true
1541
+ schema:
1542
+ type: string
1543
+ enum: [cursor]
1296
1544
  responses:
1297
1545
  "200":
1298
- description: Repositories analyzed
1546
+ description: Refresh result
1299
1547
  content:
1300
1548
  application/json:
1301
1549
  schema:
1302
- type: array
1303
- items:
1304
- $ref: "#/components/schemas/RepoReadinessScoreDto"
1550
+ $ref: "#/components/schemas/AiToolRefreshResponseDto"
1551
+ examples:
1552
+ ok:
1553
+ value: { ok: true }
1554
+ invalid_token:
1555
+ value: { ok: false, error: invalid_token }
1305
1556
  "401":
1306
1557
  $ref: "#/components/responses/Unauthorized"
1307
1558
  "403":
1308
1559
  $ref: "#/components/responses/Forbidden"
1309
1560
  "404":
1310
- description: Workspace does not exist
1561
+ description: Provider source missing for this workspace
1311
1562
  content:
1312
1563
  application/json:
1313
1564
  schema:
1314
- $ref: "#/components/schemas/ErrorMessageDto"
1565
+ $ref: "#/components/schemas/AiToolRefreshResponseDto"
1566
+ example:
1567
+ ok: false
1568
+ error: no_source
1569
+ "409":
1570
+ description: Refresh already in progress
1571
+ content:
1572
+ application/json:
1573
+ schema:
1574
+ $ref: "#/components/schemas/AiToolRefreshResponseDto"
1575
+ example:
1576
+ ok: false
1577
+ error: already_in_progress
1315
1578
 
1316
- /workspaces/{workspaceId}/readiness/analyze:
1317
- post:
1318
- tags: [RepositoryReadiness]
1319
- summary: Enqueue AI readiness analysis for one repository
1320
- description: >
1321
- User-triggered "Run now" path. Requires workspace editor/admin access and an active GitHub App
1322
- connection. Prefer `repoId`; `providerSlug` is accepted for compatibility only when it resolves
1323
- to a repository in the workspace.
1324
- operationId: postReadinessAnalyze
1579
+ /workspaces/{workspaceId}/integrations/ai-tool/{provider}/seats:
1580
+ get:
1581
+ tags: [Integrations]
1582
+ summary: List latest AI-tool seats
1583
+ operationId: getWorkspaceAiToolSeats
1325
1584
  parameters:
1326
1585
  - $ref: "#/components/parameters/WorkspaceId"
1327
- requestBody:
1328
- required: true
1329
- content:
1330
- application/json:
1331
- schema:
1332
- $ref: "#/components/schemas/ReadinessAnalyzeBodyDto"
1333
- examples:
1334
- byRepoId:
1335
- value:
1336
- repoId: "11111111-1111-4111-8111-111111111111"
1337
- byProviderSlug:
1338
- value:
1339
- providerSlug: "thinkai-llc/thinkai-platform-backend"
1586
+ - name: provider
1587
+ in: path
1588
+ required: true
1589
+ schema:
1590
+ type: string
1591
+ enum: [cursor]
1340
1592
  responses:
1341
- "202":
1342
- description: Run queued
1343
- content:
1344
- application/json:
1345
- schema:
1346
- $ref: "#/components/schemas/ReadinessAnalyzeResponseDto"
1347
- "400":
1348
- description: Invalid body, missing GitHub connection, unsupported provider, or active run conflict inputs
1593
+ "200":
1594
+ description: Seats
1349
1595
  content:
1350
1596
  application/json:
1351
1597
  schema:
1352
- $ref: "#/components/schemas/ErrorMessageDto"
1598
+ type: object
1599
+ required: [seats]
1600
+ properties:
1601
+ seats:
1602
+ type: array
1603
+ items:
1604
+ $ref: "#/components/schemas/AiToolSeatDto"
1353
1605
  "401":
1354
1606
  $ref: "#/components/responses/Unauthorized"
1355
1607
  "403":
1356
1608
  $ref: "#/components/responses/Forbidden"
1357
- "404":
1358
- description: Workspace or repository not found
1359
- content:
1360
- application/json:
1361
- schema:
1362
- $ref: "#/components/schemas/ErrorMessageDto"
1363
- "409":
1364
- description: A queued/running readiness analysis already exists for this repository slug
1365
- content:
1366
- application/json:
1367
- schema:
1368
- $ref: "#/components/schemas/ErrorMessageDto"
1369
1609
 
1370
- /workspaces/{workspaceId}/readiness/runs/{runId}:
1610
+ /workspaces/{workspaceId}/integrations/ai-tool/{provider}/daily-usage:
1371
1611
  get:
1372
- tags: [RepositoryReadiness]
1373
- summary: Get readiness analysis run status
1374
- operationId: getReadinessRun
1612
+ tags: [Integrations]
1613
+ summary: List AI-tool daily usage
1614
+ operationId: getWorkspaceAiToolDailyUsage
1375
1615
  parameters:
1376
1616
  - $ref: "#/components/parameters/WorkspaceId"
1377
- - name: runId
1617
+ - name: provider
1378
1618
  in: path
1379
1619
  required: true
1380
1620
  schema:
1381
1621
  type: string
1382
- format: uuid
1622
+ enum: [cursor]
1623
+ - name: from
1624
+ in: query
1625
+ required: false
1626
+ schema:
1627
+ type: string
1628
+ format: date
1629
+ - name: to
1630
+ in: query
1631
+ required: false
1632
+ schema:
1633
+ type: string
1634
+ format: date
1635
+ - name: externalId
1636
+ in: query
1637
+ required: false
1638
+ schema:
1639
+ type: string
1383
1640
  responses:
1384
1641
  "200":
1385
- description: Run status
1642
+ description: Usage rows
1386
1643
  content:
1387
1644
  application/json:
1388
1645
  schema:
1389
- $ref: "#/components/schemas/ReadinessRunSummaryDto"
1646
+ type: object
1647
+ required: [rows]
1648
+ properties:
1649
+ rows:
1650
+ type: array
1651
+ items:
1652
+ $ref: "#/components/schemas/AiToolDailyUsageRowDto"
1390
1653
  "401":
1391
1654
  $ref: "#/components/responses/Unauthorized"
1392
1655
  "403":
1393
1656
  $ref: "#/components/responses/Forbidden"
1394
- "404":
1395
- description: Workspace or run not found
1396
- content:
1397
- application/json:
1398
- schema:
1399
- $ref: "#/components/schemas/ErrorMessageDto"
1400
1657
 
1401
- /workspaces/{workspaceId}/readiness/repos/{repoId}:
1658
+ /workspaces/{workspaceId}/integrations/ai-tool/{provider}/spend:
1402
1659
  get:
1403
- tags: [RepositoryReadiness]
1404
- summary: Single repo readiness
1405
- description: >
1406
- Returns one stored repository scorecard with all eight dimensions and nested issues.
1407
- Unknown `repoId` or a repo that belongs to another workspace returns `404` (no cross-tenant leakage).
1408
- operationId: getReadinessRepo
1660
+ tags: [Integrations]
1661
+ summary: List AI-tool spend
1662
+ operationId: getWorkspaceAiToolSpend
1409
1663
  parameters:
1410
1664
  - $ref: "#/components/parameters/WorkspaceId"
1411
- - $ref: "#/components/parameters/RepoId"
1665
+ - name: provider
1666
+ in: path
1667
+ required: true
1668
+ schema:
1669
+ type: string
1670
+ enum: [cursor]
1671
+ - name: cycleStartMs
1672
+ in: query
1673
+ required: false
1674
+ schema:
1675
+ type: integer
1676
+ format: int64
1412
1677
  responses:
1413
1678
  "200":
1414
- description: Repo score
1415
- content:
1416
- application/json:
1417
- schema:
1418
- $ref: "#/components/schemas/RepoReadinessScoreDto"
1419
- "400":
1420
- description: Malformed `repoId` (not a UUID)
1679
+ description: Spend rows
1421
1680
  content:
1422
1681
  application/json:
1423
1682
  schema:
1424
- $ref: "#/components/schemas/ErrorMessageDto"
1683
+ type: object
1684
+ required: [rows]
1685
+ properties:
1686
+ rows:
1687
+ type: array
1688
+ items:
1689
+ $ref: "#/components/schemas/AiToolSpendRowDto"
1425
1690
  "401":
1426
1691
  $ref: "#/components/responses/Unauthorized"
1427
1692
  "403":
1428
1693
  $ref: "#/components/responses/Forbidden"
1429
- "404":
1430
- description: Workspace or repository not found
1431
- content:
1432
- application/json:
1433
- schema:
1434
- $ref: "#/components/schemas/ErrorMessageDto"
1435
1694
 
1436
- /workspaces/{workspaceId}/agentic-foundation/dashboard:
1695
+ /workspaces/{workspaceId}/integrations/ai-tool/{provider}/status:
1437
1696
  get:
1438
- tags: [AgenticFoundation]
1439
- summary: Agentic Foundation dashboard
1440
- description: >
1441
- Returns stored repository readiness scorecards plus a flattened issue queue for the
1442
- Agentic Foundation dashboard surfaces. This is read-only and does not run scoring on
1443
- the request path (issue #279). Field `generatedAt` is the server timestamp when this JSON
1444
- payload was assembled; it is not the same as per-repo `lastAnalyzed` on `RepoReadinessScoreDto`.
1445
- operationId: getAgenticFoundationDashboard
1697
+ tags: [Integrations]
1698
+ summary: Get last refresh status
1699
+ operationId: getWorkspaceAiToolRefreshStatus
1446
1700
  parameters:
1447
1701
  - $ref: "#/components/parameters/WorkspaceId"
1702
+ - name: provider
1703
+ in: path
1704
+ required: true
1705
+ schema:
1706
+ type: string
1707
+ enum: [cursor]
1448
1708
  responses:
1449
1709
  "200":
1450
- description: Dashboard scorecards and issue queue
1710
+ description: Refresh status
1451
1711
  content:
1452
1712
  application/json:
1453
1713
  schema:
1454
- $ref: "#/components/schemas/AgenticFoundationDashboardDto"
1455
- examples:
1456
- emptyWorkspace:
1457
- summary: No repos analyzed yet
1458
- value:
1459
- workspaceId: 00000000-0000-0000-0000-000000000001
1460
- generatedAt: "2026-05-12T12:00:00.000Z"
1461
- summary:
1462
- repoCount: 0
1463
- averageOverallScore: 0
1464
- issueCount: 0
1465
- criticalIssueCount: 0
1466
- repos: []
1467
- issueQueue: []
1714
+ type: object
1715
+ required: [status]
1716
+ properties:
1717
+ status:
1718
+ $ref: "#/components/schemas/AiToolRefreshStatusDto"
1468
1719
  "401":
1469
1720
  $ref: "#/components/responses/Unauthorized"
1470
1721
  "403":
1471
1722
  $ref: "#/components/responses/Forbidden"
1472
1723
  "404":
1473
- description: Workspace does not exist
1724
+ description: No refresh status yet (or provider source missing)
1474
1725
  content:
1475
1726
  application/json:
1476
1727
  schema:
1477
- $ref: "#/components/schemas/ErrorMessageDto"
1728
+ $ref: "#/components/schemas/AiToolRefreshResponseDto"
1729
+ example:
1730
+ ok: false
1731
+ error: no_source
1478
1732
 
1479
- /workspaces/{workspaceId}/agentic-foundation/agentic-readiness:
1733
+ /integrations/github/callback:
1480
1734
  get:
1481
- tags: [AgenticFoundation]
1482
- summary: Agentic readiness pillar (stored dimension slice)
1735
+ tags: [Integrations]
1736
+ summary: GitHub App installation callback
1737
+ operationId: getGithubInstallCallback
1483
1738
  description: >
1484
- Returns `RepoReadinessScoreDto` rows with only the `ai-agent-readiness` dimension populated
1485
- per repo. Same auth and persistence source as `GET .../readiness/repos` (issue #280).
1486
- operationId: getAgenticFoundationAgenticReadiness
1739
+ Public callback endpoint used by GitHub App installation flow.
1740
+ Resolves and consumes install state, persists workspace installation linkage,
1741
+ then redirects to configured SPA origin with status markers.
1487
1742
  parameters:
1488
- - $ref: "#/components/parameters/WorkspaceId"
1743
+ - name: state
1744
+ in: query
1745
+ required: true
1746
+ schema:
1747
+ type: string
1748
+ - name: installation_id
1749
+ in: query
1750
+ required: false
1751
+ schema:
1752
+ type: string
1753
+ - name: setup_action
1754
+ in: query
1755
+ required: false
1756
+ schema:
1757
+ type: string
1758
+ - name: error
1759
+ in: query
1760
+ required: false
1761
+ schema:
1762
+ type: string
1763
+ - name: error_description
1764
+ in: query
1765
+ required: false
1766
+ schema:
1767
+ type: string
1489
1768
  responses:
1490
- "200":
1491
- description: Repositories with ai-agent-readiness slice
1769
+ "302":
1770
+ description: Redirect to ThinkAI SPA return path with GitHub connection status
1771
+ "501":
1772
+ description: Workspace store not configured
1492
1773
  content:
1493
1774
  application/json:
1494
1775
  schema:
1495
- type: array
1496
- items:
1497
- $ref: "#/components/schemas/RepoReadinessScoreDto"
1498
- examples:
1499
- singleRepoSlice:
1500
- summary: One repo, single pillar dimension
1501
- value:
1502
- - repoId: 00000000-0000-0000-0000-000000000002
1503
- repoName: api-gateway
1504
- language: typescript
1505
- overallScore: 67
1506
- dimensions:
1507
- - dimensionId: ai-agent-readiness
1508
- score: 2
1509
- maxScore: 3
1510
- issues: []
1511
- lastAnalyzed: "2026-05-12T12:00:00.000Z"
1512
- teamId: null
1513
- provider:
1514
- kind: github
1515
- slug: acme/api-gateway
1516
- webUrl: https://github.com/acme/api-gateway
1517
- defaultBranch: main
1518
- externalId: null
1519
- hostBaseUrl: null
1520
- analysisStatus: idle
1521
- activeRunId: null
1522
- "401":
1523
- $ref: "#/components/responses/Unauthorized"
1524
- "403":
1525
- $ref: "#/components/responses/Forbidden"
1526
- "404":
1527
- description: Workspace does not exist
1776
+ $ref: "#/components/schemas/ErrorMessageDto"
1777
+ "503":
1778
+ description: SPA origin configuration missing
1528
1779
  content:
1529
1780
  application/json:
1530
1781
  schema:
1531
1782
  $ref: "#/components/schemas/ErrorMessageDto"
1532
1783
 
1533
- /workspaces/{workspaceId}/agentic-foundation/repository-health:
1534
- get:
1535
- tags: [AgenticFoundation]
1536
- summary: Repository health pillar (repo-hygiene slice)
1537
- description: Returns per-repo `repo-hygiene` dimension only (issue #281).
1538
- operationId: getAgenticFoundationRepositoryHealth
1784
+ /integrations/github/webhook:
1785
+ post:
1786
+ tags: [Integrations]
1787
+ summary: GitHub App webhook receiver
1788
+ operationId: postGithubWebhook
1789
+ description: >
1790
+ Public webhook endpoint that verifies `X-Hub-Signature-256` and updates or
1791
+ removes workspace GitHub integration/cache state for installation events.
1539
1792
  parameters:
1540
- - $ref: "#/components/parameters/WorkspaceId"
1793
+ - name: X-Hub-Signature-256
1794
+ in: header
1795
+ required: true
1796
+ schema:
1797
+ type: string
1798
+ - name: X-GitHub-Event
1799
+ in: header
1800
+ required: true
1801
+ schema:
1802
+ type: string
1803
+ requestBody:
1804
+ required: true
1805
+ content:
1806
+ application/json:
1807
+ schema:
1808
+ type: object
1809
+ additionalProperties: true
1541
1810
  responses:
1542
1811
  "200":
1543
- description: Repositories with repo-hygiene slice
1812
+ description: Webhook accepted
1813
+ "400":
1814
+ description: Invalid payload
1544
1815
  content:
1545
1816
  application/json:
1546
1817
  schema:
1547
- type: array
1548
- items:
1549
- $ref: "#/components/schemas/RepoReadinessScoreDto"
1818
+ $ref: "#/components/schemas/ErrorMessageDto"
1550
1819
  "401":
1551
- $ref: "#/components/responses/Unauthorized"
1552
- "403":
1553
- $ref: "#/components/responses/Forbidden"
1554
- "404":
1555
- description: Workspace does not exist
1820
+ description: Invalid webhook signature
1821
+ content:
1822
+ application/json:
1823
+ schema:
1824
+ $ref: "#/components/schemas/ErrorMessageDto"
1825
+ "501":
1826
+ description: Workspace store not configured
1827
+ content:
1828
+ application/json:
1829
+ schema:
1830
+ $ref: "#/components/schemas/ErrorMessageDto"
1831
+ "503":
1832
+ description: Missing GitHub webhook secret or GitHub App configuration
1556
1833
  content:
1557
1834
  application/json:
1558
1835
  schema:
1559
1836
  $ref: "#/components/schemas/ErrorMessageDto"
1560
1837
 
1561
- /workspaces/{workspaceId}/agentic-foundation/codebase-quality:
1838
+ /workspaces/{workspaceId}/ai-readiness:
1562
1839
  get:
1563
- tags: [AgenticFoundation]
1564
- summary: Codebase quality pillar (stack-quality slice)
1565
- description: Returns per-repo `stack-quality` dimension only (issue #282).
1566
- operationId: getAgenticFoundationCodebaseQuality
1840
+ tags: [AIReadiness]
1841
+ summary: Get AI readiness
1842
+ operationId: getAiReadiness
1567
1843
  parameters:
1568
1844
  - $ref: "#/components/parameters/WorkspaceId"
1569
1845
  responses:
1570
1846
  "200":
1571
- description: Repositories with stack-quality slice
1847
+ description: State or JSON null
1572
1848
  content:
1573
1849
  application/json:
1574
1850
  schema:
1575
- type: array
1576
- items:
1577
- $ref: "#/components/schemas/RepoReadinessScoreDto"
1851
+ $ref: "#/components/schemas/AIReadinessDto"
1578
1852
  "401":
1579
1853
  $ref: "#/components/responses/Unauthorized"
1580
- "403":
1581
- $ref: "#/components/responses/Forbidden"
1582
- "404":
1583
- description: Workspace does not exist
1854
+ put:
1855
+ tags: [AIReadiness]
1856
+ summary: Upsert AI readiness
1857
+ operationId: putAiReadiness
1858
+ parameters:
1859
+ - $ref: "#/components/parameters/WorkspaceId"
1860
+ requestBody:
1861
+ required: true
1862
+ content:
1863
+ application/json:
1864
+ schema:
1865
+ $ref: "#/components/schemas/AIReadinessDto"
1866
+ responses:
1867
+ "204":
1868
+ description: Saved
1869
+ "401":
1870
+ $ref: "#/components/responses/Unauthorized"
1871
+
1872
+ /workspaces/{workspaceId}/ai-readiness/maturity:
1873
+ post:
1874
+ tags: [AIReadiness]
1875
+ summary: Compute maturity level
1876
+ operationId: postAiReadinessMaturity
1877
+ parameters:
1878
+ - $ref: "#/components/parameters/WorkspaceId"
1879
+ requestBody:
1880
+ required: true
1881
+ content:
1882
+ application/json:
1883
+ schema:
1884
+ $ref: "#/components/schemas/AIReadinessDto"
1885
+ responses:
1886
+ "200":
1887
+ description: Derived level
1584
1888
  content:
1585
1889
  application/json:
1586
1890
  schema:
1587
- $ref: "#/components/schemas/ErrorMessageDto"
1891
+ $ref: "#/components/schemas/AiMaturityResultDto"
1892
+ "401":
1893
+ $ref: "#/components/responses/Unauthorized"
1588
1894
 
1589
- /workspaces/{workspaceId}/agentic-foundation/test-qa:
1895
+ /workspaces/{workspaceId}/tech-stack:
1590
1896
  get:
1591
- tags: [AgenticFoundation]
1592
- summary: Test and QA pillar (tests-qa slice)
1593
- description: Returns per-repo `tests-qa` dimension only (issue #283).
1594
- operationId: getAgenticFoundationTestQa
1897
+ tags: [TechStack]
1898
+ summary: Get tech stack
1899
+ operationId: getTechStack
1595
1900
  parameters:
1596
1901
  - $ref: "#/components/parameters/WorkspaceId"
1597
1902
  responses:
1598
1903
  "200":
1599
- description: Repositories with tests-qa slice
1904
+ description: >
1905
+ Saved tech stack DTO, or JSON `null` when the workspace exists but no stack
1906
+ has been saved yet. Clients should merge `null` with their empty defaults.
1600
1907
  content:
1601
1908
  application/json:
1602
1909
  schema:
1603
- type: array
1604
- items:
1605
- $ref: "#/components/schemas/RepoReadinessScoreDto"
1910
+ nullable: true
1911
+ allOf:
1912
+ - $ref: "#/components/schemas/TechStackDto"
1913
+ "400":
1914
+ description: Malformed workspaceId
1915
+ content:
1916
+ application/json:
1917
+ schema:
1918
+ $ref: "#/components/schemas/ErrorMessageDto"
1606
1919
  "401":
1607
1920
  $ref: "#/components/responses/Unauthorized"
1608
1921
  "403":
@@ -1613,24 +1926,27 @@ paths:
1613
1926
  application/json:
1614
1927
  schema:
1615
1928
  $ref: "#/components/schemas/ErrorMessageDto"
1616
-
1617
- /workspaces/{workspaceId}/agentic-foundation/observability:
1618
- get:
1619
- tags: [AgenticFoundation]
1620
- summary: Observability pillar (observability slice)
1621
- description: Returns per-repo `observability` dimension only (issue #284).
1622
- operationId: getAgenticFoundationObservability
1929
+ put:
1930
+ tags: [TechStack]
1931
+ summary: Upsert tech stack
1932
+ operationId: putTechStack
1623
1933
  parameters:
1624
1934
  - $ref: "#/components/parameters/WorkspaceId"
1935
+ requestBody:
1936
+ required: true
1937
+ content:
1938
+ application/json:
1939
+ schema:
1940
+ $ref: "#/components/schemas/TechStackDto"
1625
1941
  responses:
1626
- "200":
1627
- description: Repositories with observability slice
1942
+ "204":
1943
+ description: Saved
1944
+ "400":
1945
+ description: Body fails validation (unknown keys, bounds, missing required fields)
1628
1946
  content:
1629
1947
  application/json:
1630
1948
  schema:
1631
- type: array
1632
- items:
1633
- $ref: "#/components/schemas/RepoReadinessScoreDto"
1949
+ $ref: "#/components/schemas/ErrorMessageDto"
1634
1950
  "401":
1635
1951
  $ref: "#/components/responses/Unauthorized"
1636
1952
  "403":
@@ -1642,103 +1958,99 @@ paths:
1642
1958
  schema:
1643
1959
  $ref: "#/components/schemas/ErrorMessageDto"
1644
1960
 
1645
- /workspaces/{workspaceId}/agentic-foundation/api-contract:
1961
+ /workspaces/{workspaceId}/nda:
1646
1962
  get:
1647
- tags: [AgenticFoundation]
1648
- summary: API contract pillar (api-contracts slice)
1649
- description: Returns per-repo `api-contracts` dimension only (issue #285).
1650
- operationId: getAgenticFoundationApiContract
1963
+ tags: [NDA]
1964
+ summary: Get NDA completion state
1965
+ operationId: getNda
1651
1966
  parameters:
1652
1967
  - $ref: "#/components/parameters/WorkspaceId"
1653
1968
  responses:
1654
1969
  "200":
1655
- description: Repositories with api-contracts slice
1970
+ description: Completion or JSON null
1656
1971
  content:
1657
1972
  application/json:
1658
1973
  schema:
1659
- type: array
1660
- items:
1661
- $ref: "#/components/schemas/RepoReadinessScoreDto"
1974
+ $ref: "#/components/schemas/NdaCompletionDto"
1662
1975
  "401":
1663
1976
  $ref: "#/components/responses/Unauthorized"
1664
- "403":
1665
- $ref: "#/components/responses/Forbidden"
1666
- "404":
1667
- description: Workspace does not exist
1668
- content:
1669
- application/json:
1670
- schema:
1671
- $ref: "#/components/schemas/ErrorMessageDto"
1672
1977
 
1673
- /workspaces/{workspaceId}/agentic-foundation/platform-governance:
1978
+ /workspaces/{workspaceId}/nda/template:
1674
1979
  get:
1675
- tags: [AgenticFoundation]
1676
- summary: Platform governance pillar (platform-governance slice)
1677
- description: Returns per-repo `platform-governance` dimension only (issue #286).
1678
- operationId: getAgenticFoundationPlatformGovernance
1980
+ tags: [NDA]
1981
+ summary: NDA template for display
1982
+ operationId: getNdaTemplate
1679
1983
  parameters:
1680
1984
  - $ref: "#/components/parameters/WorkspaceId"
1681
1985
  responses:
1682
1986
  "200":
1683
- description: Repositories with platform-governance slice
1987
+ description: Template
1684
1988
  content:
1685
1989
  application/json:
1686
1990
  schema:
1687
- type: array
1688
- items:
1689
- $ref: "#/components/schemas/RepoReadinessScoreDto"
1991
+ $ref: "#/components/schemas/NdaTemplateDto"
1690
1992
  "401":
1691
1993
  $ref: "#/components/responses/Unauthorized"
1692
- "403":
1693
- $ref: "#/components/responses/Forbidden"
1694
- "404":
1695
- description: Workspace does not exist
1696
- content:
1697
- application/json:
1698
- schema:
1699
- $ref: "#/components/schemas/ErrorMessageDto"
1700
1994
 
1701
- /workspaces/{workspaceId}/agentic-foundation/delivery-governance:
1702
- get:
1703
- tags: [AgenticFoundation]
1704
- summary: Delivery governance pillar (delivery slice)
1705
- description: Returns per-repo `delivery` dimension only (issue #287).
1706
- operationId: getAgenticFoundationDeliveryGovernance
1995
+ /workspaces/{workspaceId}/nda/sign:
1996
+ post:
1997
+ tags: [NDA]
1998
+ summary: Electronic NDA signature
1999
+ operationId: postNdaSign
1707
2000
  parameters:
1708
2001
  - $ref: "#/components/parameters/WorkspaceId"
2002
+ requestBody:
2003
+ required: true
2004
+ content:
2005
+ application/json:
2006
+ schema:
2007
+ $ref: "#/components/schemas/NdaSignatureDto"
1709
2008
  responses:
1710
2009
  "200":
1711
- description: Repositories with delivery slice
2010
+ description: Completion record
1712
2011
  content:
1713
2012
  application/json:
1714
2013
  schema:
1715
- type: array
1716
- items:
1717
- $ref: "#/components/schemas/RepoReadinessScoreDto"
2014
+ $ref: "#/components/schemas/NdaCompletionDto"
1718
2015
  "401":
1719
2016
  $ref: "#/components/responses/Unauthorized"
1720
- "403":
1721
- $ref: "#/components/responses/Forbidden"
1722
- "404":
1723
- description: Workspace does not exist
2017
+
2018
+ /workspaces/{workspaceId}/nda/external:
2019
+ post:
2020
+ tags: [NDA]
2021
+ summary: External NDA path (after Supabase upload)
2022
+ description: >
2023
+ Preferred body uses `storagePath` after client upload to the `nda-documents` bucket (§14).
2024
+ Multipart variant may be supported by the server as an alternative.
2025
+ operationId: postNdaExternal
2026
+ parameters:
2027
+ - $ref: "#/components/parameters/WorkspaceId"
2028
+ requestBody:
2029
+ required: true
2030
+ content:
2031
+ application/json:
2032
+ schema:
2033
+ $ref: "#/components/schemas/NdaExternalBodyDto"
2034
+ responses:
2035
+ "200":
2036
+ description: Completion record
1724
2037
  content:
1725
2038
  application/json:
1726
2039
  schema:
1727
- $ref: "#/components/schemas/ErrorMessageDto"
2040
+ $ref: "#/components/schemas/NdaCompletionDto"
2041
+ "401":
2042
+ $ref: "#/components/responses/Unauthorized"
1728
2043
 
1729
- /workspaces/{workspaceId}/agentic-foundation/ai-resolution-hub:
2044
+ /workspaces/{workspaceId}/readiness/repos:
1730
2045
  get:
1731
- tags: [AgenticFoundation]
1732
- summary: AI resolution hub (full readiness scorecards)
1733
- description: >
1734
- Returns the same payload as `GET .../readiness/repos` (all dimensions per repo) for
1735
- cross-pillar hub views (issue #288).
1736
- operationId: getAgenticFoundationAiResolutionHub
2046
+ tags: [RepositoryReadiness]
2047
+ summary: List repo readiness scores
2048
+ operationId: listReadinessRepos
1737
2049
  parameters:
1738
2050
  - $ref: "#/components/parameters/WorkspaceId"
1739
2051
  responses:
1740
2052
  "200":
1741
- description: Full repository readiness scorecards
2053
+ description: Repositories analyzed
1742
2054
  content:
1743
2055
  application/json:
1744
2056
  schema:
@@ -1756,94 +2068,111 @@ paths:
1756
2068
  schema:
1757
2069
  $ref: "#/components/schemas/ErrorMessageDto"
1758
2070
 
1759
- /workspaces/{workspaceId}/readiness/dimensions:
1760
- get:
2071
+ /workspaces/{workspaceId}/readiness/analyze:
2072
+ post:
1761
2073
  tags: [RepositoryReadiness]
1762
- summary: Readiness dimensions catalog
2074
+ summary: Enqueue AI readiness analysis for one repository
1763
2075
  description: >
1764
- Returns static rubric metadata (id, name, description, maxScore) aligned with the readiness
1765
- scanner dimensions. Does not run scoring on the request path.
1766
- operationId: listReadinessDimensions
2076
+ User-triggered "Run now" path. Requires workspace editor/admin access and an active GitHub App
2077
+ connection. Prefer `repoId`; `providerSlug` is accepted for compatibility only when it resolves
2078
+ to a repository in the workspace.
2079
+ operationId: postReadinessAnalyze
1767
2080
  parameters:
1768
2081
  - $ref: "#/components/parameters/WorkspaceId"
2082
+ requestBody:
2083
+ required: true
2084
+ content:
2085
+ application/json:
2086
+ schema:
2087
+ $ref: "#/components/schemas/ReadinessAnalyzeBodyDto"
2088
+ examples:
2089
+ byRepoId:
2090
+ value:
2091
+ repoId: "11111111-1111-4111-8111-111111111111"
2092
+ byProviderSlug:
2093
+ value:
2094
+ providerSlug: "thinkai-llc/thinkai-platform-backend"
1769
2095
  responses:
1770
- "200":
1771
- description: Dimensions
2096
+ "202":
2097
+ description: Run queued
1772
2098
  content:
1773
2099
  application/json:
1774
2100
  schema:
1775
- type: array
1776
- items:
1777
- $ref: "#/components/schemas/ReadinessDimensionDto"
2101
+ $ref: "#/components/schemas/ReadinessAnalyzeResponseDto"
2102
+ "400":
2103
+ description: Invalid body, missing GitHub connection, unsupported provider, or active run conflict inputs
2104
+ content:
2105
+ application/json:
2106
+ schema:
2107
+ $ref: "#/components/schemas/ErrorMessageDto"
1778
2108
  "401":
1779
2109
  $ref: "#/components/responses/Unauthorized"
1780
2110
  "403":
1781
2111
  $ref: "#/components/responses/Forbidden"
1782
2112
  "404":
1783
- description: Workspace does not exist
2113
+ description: Workspace or repository not found
2114
+ content:
2115
+ application/json:
2116
+ schema:
2117
+ $ref: "#/components/schemas/ErrorMessageDto"
2118
+ "409":
2119
+ description: A queued/running readiness analysis already exists for this repository slug
1784
2120
  content:
1785
2121
  application/json:
1786
2122
  schema:
1787
2123
  $ref: "#/components/schemas/ErrorMessageDto"
1788
2124
 
1789
- /workspaces/{workspaceId}/readiness/fixes:
2125
+ /workspaces/{workspaceId}/readiness/runs/{runId}:
1790
2126
  get:
1791
2127
  tags: [RepositoryReadiness]
1792
- summary: List fix requests
1793
- description: >
1794
- Returns persisted fix-queue rows for the workspace (newest first). Empty array when none exist.
1795
- operationId: listReadinessFixes
2128
+ summary: Get readiness analysis run status
2129
+ operationId: getReadinessRun
1796
2130
  parameters:
1797
2131
  - $ref: "#/components/parameters/WorkspaceId"
2132
+ - name: runId
2133
+ in: path
2134
+ required: true
2135
+ schema:
2136
+ type: string
2137
+ format: uuid
1798
2138
  responses:
1799
2139
  "200":
1800
- description: Fix queue
2140
+ description: Run status
1801
2141
  content:
1802
2142
  application/json:
1803
2143
  schema:
1804
- type: array
1805
- items:
1806
- $ref: "#/components/schemas/FixRequestDto"
2144
+ $ref: "#/components/schemas/ReadinessRunSummaryDto"
1807
2145
  "401":
1808
2146
  $ref: "#/components/responses/Unauthorized"
1809
2147
  "403":
1810
2148
  $ref: "#/components/responses/Forbidden"
1811
2149
  "404":
1812
- description: Workspace does not exist
2150
+ description: Workspace or run not found
1813
2151
  content:
1814
2152
  application/json:
1815
2153
  schema:
1816
2154
  $ref: "#/components/schemas/ErrorMessageDto"
1817
- post:
2155
+
2156
+ /workspaces/{workspaceId}/readiness/repos/{repoId}:
2157
+ get:
1818
2158
  tags: [RepositoryReadiness]
1819
- summary: Enqueue fix for an issue
2159
+ summary: Single repo readiness
1820
2160
  description: >
1821
- Creates a fix request with `status: queued` when `issueId` belongs to `repoId` in this workspace.
1822
- Requires workspace editor or admin. Re-posting the same `issueId` and `repoId` returns the
1823
- existing row (idempotent).
1824
- operationId: postReadinessFix
2161
+ Returns one stored repository scorecard with all eight dimensions and nested issues.
2162
+ Unknown `repoId` or a repo that belongs to another workspace returns `404` (no cross-tenant leakage).
2163
+ operationId: getReadinessRepo
1825
2164
  parameters:
1826
2165
  - $ref: "#/components/parameters/WorkspaceId"
1827
- requestBody:
1828
- required: true
1829
- content:
1830
- application/json:
1831
- schema:
1832
- $ref: "#/components/schemas/CreateFixRequestBodyDto"
1833
- examples:
1834
- enqueue:
1835
- value:
1836
- issueId: "22222222-2222-2222-2222-222222222222"
1837
- repoId: "11111111-1111-4111-8111-111111111111"
2166
+ - $ref: "#/components/parameters/RepoId"
1838
2167
  responses:
1839
2168
  "200":
1840
- description: Fix request created or existing row returned
2169
+ description: Repo score
1841
2170
  content:
1842
2171
  application/json:
1843
2172
  schema:
1844
- $ref: "#/components/schemas/FixRequestDto"
2173
+ $ref: "#/components/schemas/RepoReadinessScoreDto"
1845
2174
  "400":
1846
- description: Invalid body or malformed `repoId` (not a UUID)
2175
+ description: Malformed `repoId` (not a UUID)
1847
2176
  content:
1848
2177
  application/json:
1849
2178
  schema:
@@ -1853,239 +2182,182 @@ paths:
1853
2182
  "403":
1854
2183
  $ref: "#/components/responses/Forbidden"
1855
2184
  "404":
1856
- description: Workspace not found, or issue/repo pair not in workspace
2185
+ description: Workspace or repository not found
1857
2186
  content:
1858
2187
  application/json:
1859
2188
  schema:
1860
2189
  $ref: "#/components/schemas/ErrorMessageDto"
1861
2190
 
1862
- /workspaces/{workspaceId}/readiness/fixes/{fixId}/retry:
1863
- post:
1864
- tags: [RepositoryReadiness]
1865
- summary: Retry a failed fix request
2191
+ /workspaces/{workspaceId}/agentic-foundation/dashboard:
2192
+ get:
2193
+ tags: [AgenticFoundation]
2194
+ summary: Agentic Foundation dashboard
1866
2195
  description: >
1867
- Re-queues a fix with `status: failed` when `errorCode` is one of
1868
- `FixRetryableErrorCode` (`PROVIDER_TIMEOUT`, `RATE_LIMITED`, `TRANSIENT_NETWORK`,
1869
- `RUNNER_INTERRUPTED`). Idempotent for the same failed row. Requires workspace editor or admin.
1870
- operationId: postReadinessFixRetry
2196
+ Returns stored repository readiness scorecards plus a flattened issue queue for the
2197
+ Agentic Foundation dashboard surfaces. This is read-only and does not run scoring on
2198
+ the request path (issue #279). Field `generatedAt` is the server timestamp when this JSON
2199
+ payload was assembled; it is not the same as per-repo `lastAnalyzed` on `RepoReadinessScoreDto`.
2200
+ operationId: getAgenticFoundationDashboard
1871
2201
  parameters:
1872
2202
  - $ref: "#/components/parameters/WorkspaceId"
1873
- - $ref: "#/components/parameters/FixId"
1874
2203
  responses:
1875
2204
  "200":
1876
- description: Fix re-queued
2205
+ description: Dashboard scorecards and issue queue
1877
2206
  content:
1878
2207
  application/json:
1879
2208
  schema:
1880
- $ref: "#/components/schemas/FixRequestDto"
2209
+ $ref: "#/components/schemas/AgenticFoundationDashboardDto"
2210
+ examples:
2211
+ emptyWorkspace:
2212
+ summary: No repos analyzed yet
2213
+ value:
2214
+ workspaceId: 00000000-0000-0000-0000-000000000001
2215
+ generatedAt: "2026-05-12T12:00:00.000Z"
2216
+ summary:
2217
+ repoCount: 0
2218
+ averageOverallScore: 0
2219
+ issueCount: 0
2220
+ criticalIssueCount: 0
2221
+ repos: []
2222
+ issueQueue: []
1881
2223
  "401":
1882
2224
  $ref: "#/components/responses/Unauthorized"
1883
2225
  "403":
1884
2226
  $ref: "#/components/responses/Forbidden"
1885
2227
  "404":
1886
- description: Fix not found in workspace
1887
- content:
1888
- application/json:
1889
- schema:
1890
- $ref: "#/components/schemas/ErrorMessageDto"
1891
- "409":
1892
- description: Fix is not in a retryable state
2228
+ description: Workspace does not exist
1893
2229
  content:
1894
2230
  application/json:
1895
2231
  schema:
1896
2232
  $ref: "#/components/schemas/ErrorMessageDto"
1897
2233
 
1898
- /workspaces/{workspaceId}/squad:
2234
+ /workspaces/{workspaceId}/agentic-foundation/agentic-readiness:
1899
2235
  get:
1900
- tags: [SquadProjects]
1901
- summary: Get squad configuration
1902
- operationId: getSquad
1903
- parameters:
1904
- - $ref: "#/components/parameters/WorkspaceId"
1905
- responses:
1906
- "200":
1907
- description: Squad
1908
- content:
1909
- application/json:
1910
- schema:
1911
- $ref: "#/components/schemas/SquadDto"
1912
- "401":
1913
- $ref: "#/components/responses/Unauthorized"
1914
- put:
1915
- tags: [SquadProjects]
1916
- summary: Upsert squad
1917
- operationId: putSquad
1918
- parameters:
1919
- - $ref: "#/components/parameters/WorkspaceId"
1920
- requestBody:
1921
- required: true
1922
- content:
1923
- application/json:
1924
- schema:
1925
- $ref: "#/components/schemas/SquadDto"
1926
- responses:
1927
- "204":
1928
- description: Saved
1929
- "401":
1930
- $ref: "#/components/responses/Unauthorized"
1931
-
1932
- /workspaces/{workspaceId}/projects:
1933
- get:
1934
- tags: [SquadProjects]
1935
- summary: List projects
1936
- operationId: listProjects
2236
+ tags: [AgenticFoundation]
2237
+ summary: Agentic readiness pillar (stored dimension slice)
2238
+ description: >
2239
+ Returns `RepoReadinessScoreDto` rows with only the `ai-agent-readiness` dimension populated
2240
+ per repo. Same auth and persistence source as `GET .../readiness/repos` (issue #280).
2241
+ operationId: getAgenticFoundationAgenticReadiness
1937
2242
  parameters:
1938
2243
  - $ref: "#/components/parameters/WorkspaceId"
1939
2244
  responses:
1940
2245
  "200":
1941
- description: Projects
2246
+ description: Repositories with ai-agent-readiness slice
1942
2247
  content:
1943
2248
  application/json:
1944
2249
  schema:
1945
2250
  type: array
1946
2251
  items:
1947
- $ref: "#/components/schemas/ProjectDto"
2252
+ $ref: "#/components/schemas/RepoReadinessScoreDto"
2253
+ examples:
2254
+ singleRepoSlice:
2255
+ summary: One repo, single pillar dimension
2256
+ value:
2257
+ - repoId: 00000000-0000-0000-0000-000000000002
2258
+ repoName: api-gateway
2259
+ language: typescript
2260
+ overallScore: 67
2261
+ dimensions:
2262
+ - dimensionId: ai-agent-readiness
2263
+ score: 2
2264
+ maxScore: 3
2265
+ issues: []
2266
+ lastAnalyzed: "2026-05-12T12:00:00.000Z"
2267
+ teamId: null
2268
+ provider:
2269
+ kind: github
2270
+ slug: acme/api-gateway
2271
+ webUrl: https://github.com/acme/api-gateway
2272
+ defaultBranch: main
2273
+ externalId: null
2274
+ hostBaseUrl: null
2275
+ analysisStatus: idle
2276
+ activeRunId: null
1948
2277
  "401":
1949
2278
  $ref: "#/components/responses/Unauthorized"
1950
- post:
1951
- tags: [SquadProjects]
1952
- summary: Create project
1953
- operationId: createProject
1954
- parameters:
1955
- - $ref: "#/components/parameters/WorkspaceId"
1956
- requestBody:
1957
- required: true
1958
- content:
1959
- application/json:
1960
- schema:
1961
- $ref: "#/components/schemas/ProjectDto"
1962
- responses:
1963
- "200":
1964
- description: Created project
2279
+ "403":
2280
+ $ref: "#/components/responses/Forbidden"
2281
+ "404":
2282
+ description: Workspace does not exist
1965
2283
  content:
1966
2284
  application/json:
1967
2285
  schema:
1968
- $ref: "#/components/schemas/ProjectDto"
1969
- "401":
1970
- $ref: "#/components/responses/Unauthorized"
2286
+ $ref: "#/components/schemas/ErrorMessageDto"
1971
2287
 
1972
- /workspaces/{workspaceId}/projects/{projectId}:
1973
- patch:
1974
- tags: [SquadProjects]
1975
- summary: Update project
1976
- operationId: patchProject
2288
+ /workspaces/{workspaceId}/agentic-foundation/repository-health:
2289
+ get:
2290
+ tags: [AgenticFoundation]
2291
+ summary: Repository health pillar (repo-hygiene slice)
2292
+ description: Returns per-repo `repo-hygiene` dimension only (issue #281).
2293
+ operationId: getAgenticFoundationRepositoryHealth
1977
2294
  parameters:
1978
2295
  - $ref: "#/components/parameters/WorkspaceId"
1979
- - $ref: "#/components/parameters/ProjectId"
1980
- requestBody:
1981
- required: true
1982
- content:
1983
- application/json:
1984
- schema:
1985
- $ref: "#/components/schemas/ProjectPatchDto"
1986
2296
  responses:
1987
2297
  "200":
1988
- description: Updated project
2298
+ description: Repositories with repo-hygiene slice
1989
2299
  content:
1990
2300
  application/json:
1991
2301
  schema:
1992
- $ref: "#/components/schemas/ProjectDto"
2302
+ type: array
2303
+ items:
2304
+ $ref: "#/components/schemas/RepoReadinessScoreDto"
1993
2305
  "401":
1994
2306
  $ref: "#/components/responses/Unauthorized"
2307
+ "403":
2308
+ $ref: "#/components/responses/Forbidden"
2309
+ "404":
2310
+ description: Workspace does not exist
2311
+ content:
2312
+ application/json:
2313
+ schema:
2314
+ $ref: "#/components/schemas/ErrorMessageDto"
1995
2315
 
1996
- /workspaces/{workspaceId}/notifications:
2316
+ /workspaces/{workspaceId}/agentic-foundation/codebase-quality:
1997
2317
  get:
1998
- tags: [Notifications]
1999
- summary: Paginated notifications
2000
- operationId: listNotifications
2318
+ tags: [AgenticFoundation]
2319
+ summary: Codebase quality pillar (stack-quality slice)
2320
+ description: Returns per-repo `stack-quality` dimension only (issue #282).
2321
+ operationId: getAgenticFoundationCodebaseQuality
2001
2322
  parameters:
2002
2323
  - $ref: "#/components/parameters/WorkspaceId"
2003
- - name: limit
2004
- in: query
2005
- schema: { type: integer, minimum: 1 }
2006
- - name: offset
2007
- in: query
2008
- schema: { type: integer, minimum: 0 }
2009
2324
  responses:
2010
2325
  "200":
2011
- description: Page of notifications
2326
+ description: Repositories with stack-quality slice
2012
2327
  content:
2013
2328
  application/json:
2014
2329
  schema:
2015
- $ref: "#/components/schemas/NotificationListDto"
2016
- "401":
2017
- $ref: "#/components/responses/Unauthorized"
2018
-
2019
- /workspaces/{workspaceId}/notifications/{id}/read:
2020
- post:
2021
- tags: [Notifications]
2022
- summary: Mark notification read
2023
- operationId: postNotificationRead
2024
- parameters:
2025
- - $ref: "#/components/parameters/WorkspaceId"
2026
- - $ref: "#/components/parameters/NotificationId"
2027
- responses:
2028
- "204":
2029
- description: Updated
2030
- "401":
2031
- $ref: "#/components/responses/Unauthorized"
2032
-
2033
- /workspaces/{workspaceId}/notifications/read-all:
2034
- post:
2035
- tags: [Notifications]
2036
- summary: Mark all notifications read
2037
- operationId: postNotificationsReadAll
2038
- parameters:
2039
- - $ref: "#/components/parameters/WorkspaceId"
2040
- responses:
2041
- "204":
2042
- description: Updated
2330
+ type: array
2331
+ items:
2332
+ $ref: "#/components/schemas/RepoReadinessScoreDto"
2043
2333
  "401":
2044
2334
  $ref: "#/components/responses/Unauthorized"
2045
-
2046
- /workspaces/{workspaceId}/ai-strategy/recommend:
2047
- post:
2048
- tags: [AIStrategy]
2049
- summary: Generate AI strategy recommendations
2050
- operationId: postAiStrategyRecommend
2051
- parameters:
2052
- - $ref: "#/components/parameters/WorkspaceId"
2053
- requestBody:
2054
- required: true
2055
- content:
2056
- application/json:
2057
- schema:
2058
- $ref: "#/components/schemas/AiStrategyRecommendBodyDto"
2059
- responses:
2060
- "200":
2061
- description: Plan
2335
+ "403":
2336
+ $ref: "#/components/responses/Forbidden"
2337
+ "404":
2338
+ description: Workspace does not exist
2062
2339
  content:
2063
2340
  application/json:
2064
2341
  schema:
2065
- $ref: "#/components/schemas/AiStrategyPlanDto"
2066
- "401":
2067
- $ref: "#/components/responses/Unauthorized"
2342
+ $ref: "#/components/schemas/ErrorMessageDto"
2068
2343
 
2069
- /workspaces/{workspaceId}/journey/progress:
2344
+ /workspaces/{workspaceId}/agentic-foundation/test-qa:
2070
2345
  get:
2071
- tags: [Journey]
2072
- summary: Journey step completion
2073
- operationId: getJourneyProgress
2346
+ tags: [AgenticFoundation]
2347
+ summary: Test and QA pillar (tests-qa slice)
2348
+ description: Returns per-repo `tests-qa` dimension only (issue #283).
2349
+ operationId: getAgenticFoundationTestQa
2074
2350
  parameters:
2075
2351
  - $ref: "#/components/parameters/WorkspaceId"
2076
2352
  responses:
2077
2353
  "200":
2078
- description: Progress (empty completedSteps when no row exists yet; updatedAt is synthetic in that case)
2079
- content:
2080
- application/json:
2081
- schema:
2082
- $ref: "#/components/schemas/JourneyProgressDto"
2083
- "400":
2084
- description: Malformed workspaceId
2354
+ description: Repositories with tests-qa slice
2085
2355
  content:
2086
2356
  application/json:
2087
2357
  schema:
2088
- $ref: "#/components/schemas/ErrorMessageDto"
2358
+ type: array
2359
+ items:
2360
+ $ref: "#/components/schemas/RepoReadinessScoreDto"
2089
2361
  "401":
2090
2362
  $ref: "#/components/responses/Unauthorized"
2091
2363
  "403":
@@ -2096,34 +2368,24 @@ paths:
2096
2368
  application/json:
2097
2369
  schema:
2098
2370
  $ref: "#/components/schemas/ErrorMessageDto"
2099
- put:
2100
- tags: [Journey]
2101
- summary: Replace completed steps
2102
- operationId: putJourneyProgress
2103
- description: >
2104
- Idempotent replace-all of completed route strings. Returns the persisted
2105
- JourneyProgressDto including updatedAt.
2371
+
2372
+ /workspaces/{workspaceId}/agentic-foundation/observability:
2373
+ get:
2374
+ tags: [AgenticFoundation]
2375
+ summary: Observability pillar (observability slice)
2376
+ description: Returns per-repo `observability` dimension only (issue #284).
2377
+ operationId: getAgenticFoundationObservability
2106
2378
  parameters:
2107
2379
  - $ref: "#/components/parameters/WorkspaceId"
2108
- requestBody:
2109
- required: true
2110
- content:
2111
- application/json:
2112
- schema:
2113
- $ref: "#/components/schemas/JourneyProgressUpdateDto"
2114
2380
  responses:
2115
2381
  "200":
2116
- description: Saved progress
2117
- content:
2118
- application/json:
2119
- schema:
2120
- $ref: "#/components/schemas/JourneyProgressDto"
2121
- "400":
2122
- description: Body fails validation (non-array, invalid entries, too many steps)
2382
+ description: Repositories with observability slice
2123
2383
  content:
2124
2384
  application/json:
2125
2385
  schema:
2126
- $ref: "#/components/schemas/ErrorMessageDto"
2386
+ type: array
2387
+ items:
2388
+ $ref: "#/components/schemas/RepoReadinessScoreDto"
2127
2389
  "401":
2128
2390
  $ref: "#/components/responses/Unauthorized"
2129
2391
  "403":
@@ -2135,55 +2397,186 @@ paths:
2135
2397
  schema:
2136
2398
  $ref: "#/components/schemas/ErrorMessageDto"
2137
2399
 
2138
- /me/preferences:
2400
+ /workspaces/{workspaceId}/agentic-foundation/api-contract:
2139
2401
  get:
2140
- tags: [Preferences]
2141
- summary: Get preferences for current user
2142
- operationId: getMyPreferences
2402
+ tags: [AgenticFoundation]
2403
+ summary: API contract pillar (api-contracts slice)
2404
+ description: Returns per-repo `api-contracts` dimension only (issue #285).
2405
+ operationId: getAgenticFoundationApiContract
2406
+ parameters:
2407
+ - $ref: "#/components/parameters/WorkspaceId"
2143
2408
  responses:
2144
2409
  "200":
2145
- description: Preferences
2410
+ description: Repositories with api-contracts slice
2146
2411
  content:
2147
2412
  application/json:
2148
2413
  schema:
2149
- $ref: "#/components/schemas/PreferencesDto"
2414
+ type: array
2415
+ items:
2416
+ $ref: "#/components/schemas/RepoReadinessScoreDto"
2150
2417
  "401":
2151
2418
  $ref: "#/components/responses/Unauthorized"
2152
- put:
2153
- tags: [Preferences]
2154
- summary: Upsert preferences
2155
- operationId: putMyPreferences
2156
- requestBody:
2157
- required: true
2158
- content:
2159
- application/json:
2160
- schema:
2161
- $ref: "#/components/schemas/PreferencesDto"
2419
+ "403":
2420
+ $ref: "#/components/responses/Forbidden"
2421
+ "404":
2422
+ description: Workspace does not exist
2423
+ content:
2424
+ application/json:
2425
+ schema:
2426
+ $ref: "#/components/schemas/ErrorMessageDto"
2427
+
2428
+ /workspaces/{workspaceId}/agentic-foundation/platform-governance:
2429
+ get:
2430
+ tags: [AgenticFoundation]
2431
+ summary: Platform governance pillar (platform-governance slice)
2432
+ description: Returns per-repo `platform-governance` dimension only (issue #286).
2433
+ operationId: getAgenticFoundationPlatformGovernance
2434
+ parameters:
2435
+ - $ref: "#/components/parameters/WorkspaceId"
2162
2436
  responses:
2163
2437
  "200":
2164
- description: Saved preferences
2438
+ description: Repositories with platform-governance slice
2165
2439
  content:
2166
2440
  application/json:
2167
2441
  schema:
2168
- $ref: "#/components/schemas/PreferencesDto"
2169
- "400":
2170
- description: Validation failed
2442
+ type: array
2443
+ items:
2444
+ $ref: "#/components/schemas/RepoReadinessScoreDto"
2445
+ "401":
2446
+ $ref: "#/components/responses/Unauthorized"
2447
+ "403":
2448
+ $ref: "#/components/responses/Forbidden"
2449
+ "404":
2450
+ description: Workspace does not exist
2171
2451
  content:
2172
2452
  application/json:
2173
2453
  schema:
2174
- $ref: "#/components/schemas/PreferencesValidationErrorDto"
2454
+ $ref: "#/components/schemas/ErrorMessageDto"
2455
+
2456
+ /workspaces/{workspaceId}/agentic-foundation/delivery-governance:
2457
+ get:
2458
+ tags: [AgenticFoundation]
2459
+ summary: Delivery governance pillar (delivery slice)
2460
+ description: Returns per-repo `delivery` dimension only (issue #287).
2461
+ operationId: getAgenticFoundationDeliveryGovernance
2462
+ parameters:
2463
+ - $ref: "#/components/parameters/WorkspaceId"
2464
+ responses:
2465
+ "200":
2466
+ description: Repositories with delivery slice
2467
+ content:
2468
+ application/json:
2469
+ schema:
2470
+ type: array
2471
+ items:
2472
+ $ref: "#/components/schemas/RepoReadinessScoreDto"
2175
2473
  "401":
2176
2474
  $ref: "#/components/responses/Unauthorized"
2475
+ "403":
2476
+ $ref: "#/components/responses/Forbidden"
2477
+ "404":
2478
+ description: Workspace does not exist
2479
+ content:
2480
+ application/json:
2481
+ schema:
2482
+ $ref: "#/components/schemas/ErrorMessageDto"
2177
2483
 
2178
- /workspaces/{workspaceId}/org-chart/import:
2484
+ /workspaces/{workspaceId}/agentic-foundation/ai-resolution-hub:
2485
+ get:
2486
+ tags: [AgenticFoundation]
2487
+ summary: AI resolution hub (full readiness scorecards)
2488
+ description: >
2489
+ Returns the same payload as `GET .../readiness/repos` (all dimensions per repo) for
2490
+ cross-pillar hub views (issue #288).
2491
+ operationId: getAgenticFoundationAiResolutionHub
2492
+ parameters:
2493
+ - $ref: "#/components/parameters/WorkspaceId"
2494
+ responses:
2495
+ "200":
2496
+ description: Full repository readiness scorecards
2497
+ content:
2498
+ application/json:
2499
+ schema:
2500
+ type: array
2501
+ items:
2502
+ $ref: "#/components/schemas/RepoReadinessScoreDto"
2503
+ "401":
2504
+ $ref: "#/components/responses/Unauthorized"
2505
+ "403":
2506
+ $ref: "#/components/responses/Forbidden"
2507
+ "404":
2508
+ description: Workspace does not exist
2509
+ content:
2510
+ application/json:
2511
+ schema:
2512
+ $ref: "#/components/schemas/ErrorMessageDto"
2513
+
2514
+ /workspaces/{workspaceId}/readiness/dimensions:
2515
+ get:
2516
+ tags: [RepositoryReadiness]
2517
+ summary: Readiness dimensions catalog
2518
+ description: >
2519
+ Returns static rubric metadata (id, name, description, maxScore) aligned with the readiness
2520
+ scanner dimensions. Does not run scoring on the request path.
2521
+ operationId: listReadinessDimensions
2522
+ parameters:
2523
+ - $ref: "#/components/parameters/WorkspaceId"
2524
+ responses:
2525
+ "200":
2526
+ description: Dimensions
2527
+ content:
2528
+ application/json:
2529
+ schema:
2530
+ type: array
2531
+ items:
2532
+ $ref: "#/components/schemas/ReadinessDimensionDto"
2533
+ "401":
2534
+ $ref: "#/components/responses/Unauthorized"
2535
+ "403":
2536
+ $ref: "#/components/responses/Forbidden"
2537
+ "404":
2538
+ description: Workspace does not exist
2539
+ content:
2540
+ application/json:
2541
+ schema:
2542
+ $ref: "#/components/schemas/ErrorMessageDto"
2543
+
2544
+ /workspaces/{workspaceId}/readiness/fixes:
2545
+ get:
2546
+ tags: [RepositoryReadiness]
2547
+ summary: List fix requests
2548
+ description: >
2549
+ Returns persisted fix-queue rows for the workspace (newest first). Empty array when none exist.
2550
+ operationId: listReadinessFixes
2551
+ parameters:
2552
+ - $ref: "#/components/parameters/WorkspaceId"
2553
+ responses:
2554
+ "200":
2555
+ description: Fix queue
2556
+ content:
2557
+ application/json:
2558
+ schema:
2559
+ type: array
2560
+ items:
2561
+ $ref: "#/components/schemas/FixRequestDto"
2562
+ "401":
2563
+ $ref: "#/components/responses/Unauthorized"
2564
+ "403":
2565
+ $ref: "#/components/responses/Forbidden"
2566
+ "404":
2567
+ description: Workspace does not exist
2568
+ content:
2569
+ application/json:
2570
+ schema:
2571
+ $ref: "#/components/schemas/ErrorMessageDto"
2179
2572
  post:
2180
- tags: [OrgChartImport]
2181
- summary: Import org chart from Supabase storage path
2573
+ tags: [RepositoryReadiness]
2574
+ summary: Enqueue fix for an issue
2182
2575
  description: >
2183
- Parse-only. Downloads the CSV at `storagePath` from the `org-charts` bucket and returns
2184
- `persons` and `errors`. Does not persist to `spa_org_chart` or `org_chart`. The SPA
2185
- derives teams client-side and saves the org structure via `POST /workspaces/{workspaceId}/org-chart`.
2186
- operationId: postOrgChartImport
2576
+ Creates a fix request with `status: queued` when `issueId` belongs to `repoId` in this workspace.
2577
+ Requires workspace editor or admin. Re-posting the same `issueId` and `repoId` returns the
2578
+ existing row (idempotent).
2579
+ operationId: postReadinessFix
2187
2580
  parameters:
2188
2581
  - $ref: "#/components/parameters/WorkspaceId"
2189
2582
  requestBody:
@@ -2191,16 +2584,21 @@ paths:
2191
2584
  content:
2192
2585
  application/json:
2193
2586
  schema:
2194
- $ref: "#/components/schemas/OrgChartImportBodyDto"
2587
+ $ref: "#/components/schemas/CreateFixRequestBodyDto"
2588
+ examples:
2589
+ enqueue:
2590
+ value:
2591
+ issueId: "22222222-2222-2222-2222-222222222222"
2592
+ repoId: "11111111-1111-4111-8111-111111111111"
2195
2593
  responses:
2196
2594
  "200":
2197
- description: Parsed persons from uploaded CSV (not persisted server-side)
2595
+ description: Fix request created or existing row returned
2198
2596
  content:
2199
2597
  application/json:
2200
2598
  schema:
2201
- $ref: "#/components/schemas/OrgChartImportResultDto"
2599
+ $ref: "#/components/schemas/FixRequestDto"
2202
2600
  "400":
2203
- description: Invalid body or storage path
2601
+ description: Invalid body or malformed `repoId` (not a UUID)
2204
2602
  content:
2205
2603
  application/json:
2206
2604
  schema:
@@ -2210,57 +2608,417 @@ paths:
2210
2608
  "403":
2211
2609
  $ref: "#/components/responses/Forbidden"
2212
2610
  "404":
2213
- description: Storage object not found
2611
+ description: Workspace not found, or issue/repo pair not in workspace
2214
2612
  content:
2215
2613
  application/json:
2216
2614
  schema:
2217
2615
  $ref: "#/components/schemas/ErrorMessageDto"
2218
- "502":
2219
- description: Failed to download file from storage
2616
+
2617
+ /workspaces/{workspaceId}/readiness/fixes/{fixId}/retry:
2618
+ post:
2619
+ tags: [RepositoryReadiness]
2620
+ summary: Retry a failed fix request
2621
+ description: >
2622
+ Re-queues a fix with `status: failed` when `errorCode` is one of
2623
+ `FixRetryableErrorCode` (`PROVIDER_TIMEOUT`, `RATE_LIMITED`, `TRANSIENT_NETWORK`,
2624
+ `RUNNER_INTERRUPTED`). Idempotent for the same failed row. Requires workspace editor or admin.
2625
+ operationId: postReadinessFixRetry
2626
+ parameters:
2627
+ - $ref: "#/components/parameters/WorkspaceId"
2628
+ - $ref: "#/components/parameters/FixId"
2629
+ responses:
2630
+ "200":
2631
+ description: Fix re-queued
2632
+ content:
2633
+ application/json:
2634
+ schema:
2635
+ $ref: "#/components/schemas/FixRequestDto"
2636
+ "401":
2637
+ $ref: "#/components/responses/Unauthorized"
2638
+ "403":
2639
+ $ref: "#/components/responses/Forbidden"
2640
+ "404":
2641
+ description: Fix not found in workspace
2220
2642
  content:
2221
2643
  application/json:
2222
2644
  schema:
2223
2645
  $ref: "#/components/schemas/ErrorMessageDto"
2224
- "503":
2225
- description: Storage import not configured or service unavailable
2646
+ "409":
2647
+ description: Fix is not in a retryable state
2226
2648
  content:
2227
2649
  application/json:
2228
2650
  schema:
2229
2651
  $ref: "#/components/schemas/ErrorMessageDto"
2230
2652
 
2231
- components:
2232
- securitySchemes:
2233
- bearerAuth:
2234
- type: http
2235
- scheme: bearer
2236
- bearerFormat: JWT
2237
- description: Supabase access token (`Authorization` header).
2238
-
2239
- parameters:
2240
- WorkspaceId:
2241
- name: workspaceId
2242
- in: path
2243
- required: true
2244
- schema:
2245
- type: string
2246
- RepoId:
2247
- name: repoId
2248
- in: path
2249
- required: true
2250
- schema:
2251
- type: string
2252
- format: uuid
2253
- ProjectId:
2254
- name: projectId
2255
- in: path
2256
- required: true
2257
- schema:
2258
- type: string
2259
- NotificationId:
2260
- name: id
2261
- in: path
2262
- required: true
2263
- schema:
2653
+ /workspaces/{workspaceId}/squad:
2654
+ get:
2655
+ tags: [SquadProjects]
2656
+ summary: Get squad configuration
2657
+ operationId: getSquad
2658
+ parameters:
2659
+ - $ref: "#/components/parameters/WorkspaceId"
2660
+ responses:
2661
+ "200":
2662
+ description: Squad
2663
+ content:
2664
+ application/json:
2665
+ schema:
2666
+ $ref: "#/components/schemas/SquadDto"
2667
+ "401":
2668
+ $ref: "#/components/responses/Unauthorized"
2669
+ put:
2670
+ tags: [SquadProjects]
2671
+ summary: Upsert squad
2672
+ operationId: putSquad
2673
+ parameters:
2674
+ - $ref: "#/components/parameters/WorkspaceId"
2675
+ requestBody:
2676
+ required: true
2677
+ content:
2678
+ application/json:
2679
+ schema:
2680
+ $ref: "#/components/schemas/SquadDto"
2681
+ responses:
2682
+ "204":
2683
+ description: Saved
2684
+ "401":
2685
+ $ref: "#/components/responses/Unauthorized"
2686
+
2687
+ /workspaces/{workspaceId}/projects:
2688
+ get:
2689
+ tags: [SquadProjects]
2690
+ summary: List projects
2691
+ operationId: listProjects
2692
+ parameters:
2693
+ - $ref: "#/components/parameters/WorkspaceId"
2694
+ responses:
2695
+ "200":
2696
+ description: Projects
2697
+ content:
2698
+ application/json:
2699
+ schema:
2700
+ type: array
2701
+ items:
2702
+ $ref: "#/components/schemas/ProjectDto"
2703
+ "401":
2704
+ $ref: "#/components/responses/Unauthorized"
2705
+ post:
2706
+ tags: [SquadProjects]
2707
+ summary: Create project
2708
+ operationId: createProject
2709
+ parameters:
2710
+ - $ref: "#/components/parameters/WorkspaceId"
2711
+ requestBody:
2712
+ required: true
2713
+ content:
2714
+ application/json:
2715
+ schema:
2716
+ $ref: "#/components/schemas/ProjectDto"
2717
+ responses:
2718
+ "200":
2719
+ description: Created project
2720
+ content:
2721
+ application/json:
2722
+ schema:
2723
+ $ref: "#/components/schemas/ProjectDto"
2724
+ "401":
2725
+ $ref: "#/components/responses/Unauthorized"
2726
+
2727
+ /workspaces/{workspaceId}/projects/{projectId}:
2728
+ patch:
2729
+ tags: [SquadProjects]
2730
+ summary: Update project
2731
+ operationId: patchProject
2732
+ parameters:
2733
+ - $ref: "#/components/parameters/WorkspaceId"
2734
+ - $ref: "#/components/parameters/ProjectId"
2735
+ requestBody:
2736
+ required: true
2737
+ content:
2738
+ application/json:
2739
+ schema:
2740
+ $ref: "#/components/schemas/ProjectPatchDto"
2741
+ responses:
2742
+ "200":
2743
+ description: Updated project
2744
+ content:
2745
+ application/json:
2746
+ schema:
2747
+ $ref: "#/components/schemas/ProjectDto"
2748
+ "401":
2749
+ $ref: "#/components/responses/Unauthorized"
2750
+
2751
+ /workspaces/{workspaceId}/notifications:
2752
+ get:
2753
+ tags: [Notifications]
2754
+ summary: Paginated notifications
2755
+ operationId: listNotifications
2756
+ parameters:
2757
+ - $ref: "#/components/parameters/WorkspaceId"
2758
+ - name: limit
2759
+ in: query
2760
+ schema: { type: integer, minimum: 1 }
2761
+ - name: offset
2762
+ in: query
2763
+ schema: { type: integer, minimum: 0 }
2764
+ responses:
2765
+ "200":
2766
+ description: Page of notifications
2767
+ content:
2768
+ application/json:
2769
+ schema:
2770
+ $ref: "#/components/schemas/NotificationListDto"
2771
+ "401":
2772
+ $ref: "#/components/responses/Unauthorized"
2773
+
2774
+ /workspaces/{workspaceId}/notifications/{id}/read:
2775
+ post:
2776
+ tags: [Notifications]
2777
+ summary: Mark notification read
2778
+ operationId: postNotificationRead
2779
+ parameters:
2780
+ - $ref: "#/components/parameters/WorkspaceId"
2781
+ - $ref: "#/components/parameters/NotificationId"
2782
+ responses:
2783
+ "204":
2784
+ description: Updated
2785
+ "401":
2786
+ $ref: "#/components/responses/Unauthorized"
2787
+
2788
+ /workspaces/{workspaceId}/notifications/read-all:
2789
+ post:
2790
+ tags: [Notifications]
2791
+ summary: Mark all notifications read
2792
+ operationId: postNotificationsReadAll
2793
+ parameters:
2794
+ - $ref: "#/components/parameters/WorkspaceId"
2795
+ responses:
2796
+ "204":
2797
+ description: Updated
2798
+ "401":
2799
+ $ref: "#/components/responses/Unauthorized"
2800
+
2801
+ /workspaces/{workspaceId}/ai-strategy/recommend:
2802
+ post:
2803
+ tags: [AIStrategy]
2804
+ summary: Generate AI strategy recommendations
2805
+ operationId: postAiStrategyRecommend
2806
+ parameters:
2807
+ - $ref: "#/components/parameters/WorkspaceId"
2808
+ requestBody:
2809
+ required: true
2810
+ content:
2811
+ application/json:
2812
+ schema:
2813
+ $ref: "#/components/schemas/AiStrategyRecommendBodyDto"
2814
+ responses:
2815
+ "200":
2816
+ description: Plan
2817
+ content:
2818
+ application/json:
2819
+ schema:
2820
+ $ref: "#/components/schemas/AiStrategyPlanDto"
2821
+ "401":
2822
+ $ref: "#/components/responses/Unauthorized"
2823
+
2824
+ /workspaces/{workspaceId}/journey/progress:
2825
+ get:
2826
+ tags: [Journey]
2827
+ summary: Journey step completion
2828
+ operationId: getJourneyProgress
2829
+ parameters:
2830
+ - $ref: "#/components/parameters/WorkspaceId"
2831
+ responses:
2832
+ "200":
2833
+ description: Progress (empty completedSteps when no row exists yet; updatedAt is synthetic in that case)
2834
+ content:
2835
+ application/json:
2836
+ schema:
2837
+ $ref: "#/components/schemas/JourneyProgressDto"
2838
+ "400":
2839
+ description: Malformed workspaceId
2840
+ content:
2841
+ application/json:
2842
+ schema:
2843
+ $ref: "#/components/schemas/ErrorMessageDto"
2844
+ "401":
2845
+ $ref: "#/components/responses/Unauthorized"
2846
+ "403":
2847
+ $ref: "#/components/responses/Forbidden"
2848
+ "404":
2849
+ description: Workspace does not exist
2850
+ content:
2851
+ application/json:
2852
+ schema:
2853
+ $ref: "#/components/schemas/ErrorMessageDto"
2854
+ put:
2855
+ tags: [Journey]
2856
+ summary: Replace completed steps
2857
+ operationId: putJourneyProgress
2858
+ description: >
2859
+ Idempotent replace-all of completed route strings. Returns the persisted
2860
+ JourneyProgressDto including updatedAt.
2861
+ parameters:
2862
+ - $ref: "#/components/parameters/WorkspaceId"
2863
+ requestBody:
2864
+ required: true
2865
+ content:
2866
+ application/json:
2867
+ schema:
2868
+ $ref: "#/components/schemas/JourneyProgressUpdateDto"
2869
+ responses:
2870
+ "200":
2871
+ description: Saved progress
2872
+ content:
2873
+ application/json:
2874
+ schema:
2875
+ $ref: "#/components/schemas/JourneyProgressDto"
2876
+ "400":
2877
+ description: Body fails validation (non-array, invalid entries, too many steps)
2878
+ content:
2879
+ application/json:
2880
+ schema:
2881
+ $ref: "#/components/schemas/ErrorMessageDto"
2882
+ "401":
2883
+ $ref: "#/components/responses/Unauthorized"
2884
+ "403":
2885
+ $ref: "#/components/responses/Forbidden"
2886
+ "404":
2887
+ description: Workspace does not exist
2888
+ content:
2889
+ application/json:
2890
+ schema:
2891
+ $ref: "#/components/schemas/ErrorMessageDto"
2892
+
2893
+ /me/preferences:
2894
+ get:
2895
+ tags: [Preferences]
2896
+ summary: Get preferences for current user
2897
+ operationId: getMyPreferences
2898
+ responses:
2899
+ "200":
2900
+ description: Preferences
2901
+ content:
2902
+ application/json:
2903
+ schema:
2904
+ $ref: "#/components/schemas/PreferencesDto"
2905
+ "401":
2906
+ $ref: "#/components/responses/Unauthorized"
2907
+ put:
2908
+ tags: [Preferences]
2909
+ summary: Upsert preferences
2910
+ operationId: putMyPreferences
2911
+ requestBody:
2912
+ required: true
2913
+ content:
2914
+ application/json:
2915
+ schema:
2916
+ $ref: "#/components/schemas/PreferencesDto"
2917
+ responses:
2918
+ "200":
2919
+ description: Saved preferences
2920
+ content:
2921
+ application/json:
2922
+ schema:
2923
+ $ref: "#/components/schemas/PreferencesDto"
2924
+ "400":
2925
+ description: Validation failed
2926
+ content:
2927
+ application/json:
2928
+ schema:
2929
+ $ref: "#/components/schemas/PreferencesValidationErrorDto"
2930
+ "401":
2931
+ $ref: "#/components/responses/Unauthorized"
2932
+
2933
+ /workspaces/{workspaceId}/org-chart/import:
2934
+ post:
2935
+ tags: [OrgChartImport]
2936
+ summary: Import org chart from Supabase storage path
2937
+ description: >
2938
+ Parse-only. Downloads the CSV at `storagePath` from the `org-charts` bucket and returns
2939
+ `persons` and `errors`. Does not persist to `spa_org_chart` or `org_chart`. The SPA
2940
+ derives teams client-side and saves the org structure via `POST /workspaces/{workspaceId}/org-chart`.
2941
+ Required columns: employee_id, name, email, title, department, team, manager_id, location.
2942
+ Optional: github_username (GitHub login for productivity ingest). Saving the SPA snapshot
2943
+ also materializes a scoring `org_chart` tree for team/person metrics when import members are present.
2944
+ operationId: postOrgChartImport
2945
+ parameters:
2946
+ - $ref: "#/components/parameters/WorkspaceId"
2947
+ requestBody:
2948
+ required: true
2949
+ content:
2950
+ application/json:
2951
+ schema:
2952
+ $ref: "#/components/schemas/OrgChartImportBodyDto"
2953
+ responses:
2954
+ "200":
2955
+ description: Parsed persons from uploaded CSV (not persisted server-side)
2956
+ content:
2957
+ application/json:
2958
+ schema:
2959
+ $ref: "#/components/schemas/OrgChartImportResultDto"
2960
+ "400":
2961
+ description: Invalid body or storage path
2962
+ content:
2963
+ application/json:
2964
+ schema:
2965
+ $ref: "#/components/schemas/ErrorMessageDto"
2966
+ "401":
2967
+ $ref: "#/components/responses/Unauthorized"
2968
+ "403":
2969
+ $ref: "#/components/responses/Forbidden"
2970
+ "404":
2971
+ description: Storage object not found
2972
+ content:
2973
+ application/json:
2974
+ schema:
2975
+ $ref: "#/components/schemas/ErrorMessageDto"
2976
+ "502":
2977
+ description: Failed to download file from storage
2978
+ content:
2979
+ application/json:
2980
+ schema:
2981
+ $ref: "#/components/schemas/ErrorMessageDto"
2982
+ "503":
2983
+ description: Storage import not configured or service unavailable
2984
+ content:
2985
+ application/json:
2986
+ schema:
2987
+ $ref: "#/components/schemas/ErrorMessageDto"
2988
+
2989
+ components:
2990
+ securitySchemes:
2991
+ bearerAuth:
2992
+ type: http
2993
+ scheme: bearer
2994
+ bearerFormat: JWT
2995
+ description: Supabase access token (`Authorization` header).
2996
+
2997
+ parameters:
2998
+ WorkspaceId:
2999
+ name: workspaceId
3000
+ in: path
3001
+ required: true
3002
+ schema:
3003
+ type: string
3004
+ RepoId:
3005
+ name: repoId
3006
+ in: path
3007
+ required: true
3008
+ schema:
3009
+ type: string
3010
+ format: uuid
3011
+ ProjectId:
3012
+ name: projectId
3013
+ in: path
3014
+ required: true
3015
+ schema:
3016
+ type: string
3017
+ NotificationId:
3018
+ name: id
3019
+ in: path
3020
+ required: true
3021
+ schema:
2264
3022
  type: string
2265
3023
  FixId:
2266
3024
  name: fixId
@@ -2269,390 +3027,1305 @@ components:
2269
3027
  schema:
2270
3028
  type: string
2271
3029
  format: uuid
3030
+ InsightsDepartmentFilter:
3031
+ name: department
3032
+ in: query
3033
+ description: >
3034
+ Department filter; send `All departments` to include every department. Scoped using
3035
+ imported `thinkai_org_structure` members when present, otherwise SPA org chart
3036
+ `department` nodes and their descendant teams. When neither source defines the
3037
+ requested department, insights return no scoped teams or contributors (not org-wide
3038
+ data).
3039
+ schema:
3040
+ type: string
3041
+ default: All departments
3042
+
3043
+ responses:
3044
+ Unauthorized:
3045
+ description: Missing or invalid bearer token
3046
+ content:
3047
+ application/json:
3048
+ schema:
3049
+ $ref: "#/components/schemas/ErrorMessageDto"
3050
+ Forbidden:
3051
+ description: Authenticated but not allowed
3052
+ content:
3053
+ application/json:
3054
+ schema:
3055
+ $ref: "#/components/schemas/ErrorMessageDto"
3056
+
3057
+ schemas:
3058
+ ErrorMessageDto:
3059
+ type: object
3060
+ required: [error]
3061
+ properties:
3062
+ error:
3063
+ type: string
3064
+
3065
+ TeamMemberDto:
3066
+ type: object
3067
+ required: [id, name, email, role]
3068
+ properties:
3069
+ id:
3070
+ type: string
3071
+ name:
3072
+ type: string
3073
+ email:
3074
+ type: string
3075
+ role:
3076
+ type: string
3077
+ enum: [admin, editor, viewer]
3078
+
3079
+ TeamMemberListDto:
3080
+ type: object
3081
+ required: [members]
3082
+ properties:
3083
+ members:
3084
+ type: array
3085
+ items:
3086
+ $ref: "#/components/schemas/TeamMemberDto"
3087
+
3088
+ InviteTeamMemberBodyDto:
3089
+ type: object
3090
+ required: [email, role]
3091
+ properties:
3092
+ email:
3093
+ type: string
3094
+ role:
3095
+ type: string
3096
+ enum: [admin, editor, viewer]
3097
+
3098
+ UpdateTeamMemberRoleBodyDto:
3099
+ type: object
3100
+ required: [role]
3101
+ properties:
3102
+ role:
3103
+ type: string
3104
+ enum: [admin, editor, viewer]
3105
+
3106
+ OrgChartNodeDto:
3107
+ type: object
3108
+ additionalProperties: false
3109
+ required: [id, name, type]
3110
+ properties:
3111
+ id:
3112
+ type: string
3113
+ name:
3114
+ type: string
3115
+ type:
3116
+ type: string
3117
+ enum: [org, department, team, person]
3118
+ external_ids:
3119
+ type: object
3120
+ additionalProperties: { type: string }
3121
+ children:
3122
+ type: array
3123
+ items:
3124
+ $ref: "#/components/schemas/OrgChartNodeDto"
3125
+
3126
+ OrgChartTreeNodeDto:
3127
+ type: object
3128
+ required: [id]
3129
+ properties:
3130
+ id:
3131
+ type: string
3132
+ name:
3133
+ type: string
3134
+ type:
3135
+ type: string
3136
+ person_id:
3137
+ type: string
3138
+ team:
3139
+ type: string
3140
+ role:
3141
+ type: string
3142
+ level:
3143
+ type: string
3144
+ external_ids:
3145
+ type: object
3146
+ additionalProperties: { type: string }
3147
+ children:
3148
+ type: array
3149
+ items:
3150
+ $ref: "#/components/schemas/OrgChartTreeNodeDto"
3151
+
3152
+ ScoringOrgChartDto:
3153
+ type: object
3154
+ additionalProperties: false
3155
+ required: [root]
3156
+ properties:
3157
+ root:
3158
+ $ref: "#/components/schemas/OrgChartTreeNodeDto"
3159
+
3160
+ OrgChartDto:
3161
+ type: object
3162
+ additionalProperties: false
3163
+ required: [root]
3164
+ properties:
3165
+ root:
3166
+ $ref: "#/components/schemas/OrgChartNodeDto"
3167
+
3168
+ OrgChartPersonDto:
3169
+ type: object
3170
+ additionalProperties: false
3171
+ required: [employeeId, name, email, title, department, team, managerId, location]
3172
+ properties:
3173
+ employeeId:
3174
+ type: string
3175
+ description: Stable HR identifier; referenced by manager_id for reporting lines.
3176
+ name:
3177
+ type: string
3178
+ email:
3179
+ type: string
3180
+ description: Work email; used as canonical person_id in productivity ingest when present.
3181
+ title:
3182
+ type: string
3183
+ description: Job title; used for contributor rows and leadership (enabler) hints.
3184
+ department:
3185
+ type: string
3186
+ description: Department filter on Productivity insights.
3187
+ team:
3188
+ type: string
3189
+ description: Engineering team name for Aggregated Teams and team drill-downs.
3190
+ managerId:
3191
+ type: string
3192
+ description: employee_id of the person's manager; leave empty for top-level leaders.
3193
+ location:
3194
+ type: string
3195
+ githubUsername:
3196
+ type: string
3197
+ description: GitHub login (no @) to map merged PRs and reviews to this person in productivity ingest.
3198
+
3199
+ OrgChartImportResultDto:
3200
+ type: object
3201
+ additionalProperties: false
3202
+ required: [persons, errors]
3203
+ properties:
3204
+ persons:
3205
+ type: array
3206
+ items:
3207
+ $ref: "#/components/schemas/OrgChartPersonDto"
3208
+ errors:
3209
+ type: array
3210
+ items:
3211
+ type: string
3212
+
3213
+ OrgChartSaveResultDto:
3214
+ type: object
3215
+ additionalProperties: false
3216
+ required: [ok, tenantId]
3217
+ properties:
3218
+ ok:
3219
+ type: boolean
3220
+ tenantId:
3221
+ type: string
3222
+ format: uuid
3223
+
3224
+ TenantSourceEntryDto:
3225
+ type: object
3226
+ required: [type]
3227
+ properties:
3228
+ type:
3229
+ type: string
3230
+ additionalProperties: true
3231
+
3232
+ CursorSourceDto:
3233
+ type: object
3234
+ description: >
3235
+ Cursor (AI Tools) source entry for `PUT /workspaces/{workspaceId}/sources`
3236
+ and `POST /workspaces/{workspaceId}/sources/test`. The `token` field
3237
+ carries a literal Cursor API key on writes; reads from
3238
+ `GET /workspaces/{workspaceId}/config` redact it to `***`.
3239
+ Runtime calls use the Cursor Admin API at `https://api.cursor.com` (HTTP Basic); do not rely on `baseUrl`.
3240
+ required: [type, token]
3241
+ additionalProperties: false
3242
+ properties:
3243
+ type:
3244
+ type: string
3245
+ enum: [cursor]
3246
+ token:
3247
+ type: string
3248
+ minLength: 1
3249
+ maxLength: 256
3250
+ description: Cursor API key (literal). Whitespace and `env:` prefix are rejected.
3251
+ baseUrl:
3252
+ type: string
3253
+ deprecated: true
3254
+ description: >
3255
+ Deprecated. Historically suggested an API base URL; the server ignores this field
3256
+ and always uses the Cursor Admin API host. Kept so older clients can submit payloads unchanged.
3257
+ default: "https://api.cursor.com"
3258
+
3259
+ WorkspaceConfigDto:
3260
+ type: object
3261
+ required: [sources]
3262
+ properties:
3263
+ sources:
3264
+ type: array
3265
+ items:
3266
+ $ref: "#/components/schemas/TenantSourceEntryDto"
3267
+ orgChart:
3268
+ nullable: true
3269
+ allOf:
3270
+ - $ref: "#/components/schemas/ScoringOrgChartDto"
3271
+ region:
3272
+ type: string
3273
+ nullable: true
3274
+ enum: [us, eu, me]
3275
+
3276
+ TestConnectionResponseDto:
3277
+ type: object
3278
+ required: [success]
3279
+ properties:
3280
+ success:
3281
+ type: boolean
3282
+ error:
3283
+ type: string
3284
+
3285
+ AiToolSeatDto:
3286
+ type: object
3287
+ required: [externalId, email, isRemoved]
3288
+ properties:
3289
+ externalId:
3290
+ type: string
3291
+ email:
3292
+ type: string
3293
+ displayName:
3294
+ type: string
3295
+ nullable: true
3296
+ role:
3297
+ type: string
3298
+ nullable: true
3299
+ isRemoved:
3300
+ type: boolean
3301
+ payload:
3302
+ type: object
3303
+ additionalProperties: true
2272
3304
 
2273
- responses:
2274
- Unauthorized:
2275
- description: Missing or invalid bearer token
2276
- content:
2277
- application/json:
2278
- schema:
2279
- $ref: "#/components/schemas/ErrorMessageDto"
2280
- Forbidden:
2281
- description: Authenticated but not allowed
2282
- content:
2283
- application/json:
2284
- schema:
2285
- $ref: "#/components/schemas/ErrorMessageDto"
3305
+ AiToolDailyUsageRowDto:
3306
+ type: object
3307
+ required: [day, externalId, isActive]
3308
+ properties:
3309
+ day:
3310
+ type: string
3311
+ format: date
3312
+ externalId:
3313
+ type: string
3314
+ email:
3315
+ type: string
3316
+ nullable: true
3317
+ isActive:
3318
+ type: boolean
3319
+ mostUsedModel:
3320
+ type: string
3321
+ nullable: true
3322
+ payload:
3323
+ type: object
3324
+ additionalProperties: true
3325
+
3326
+ AiToolSpendRowDto:
3327
+ type: object
3328
+ required: [billingCycleStartMs, externalId, spendCents]
3329
+ properties:
3330
+ billingCycleStartMs:
3331
+ type: integer
3332
+ format: int64
3333
+ externalId:
3334
+ type: string
3335
+ email:
3336
+ type: string
3337
+ nullable: true
3338
+ spendCents:
3339
+ type: integer
3340
+ format: int64
3341
+ payload:
3342
+ type: object
3343
+ additionalProperties: true
3344
+
3345
+ AiToolRefreshStatusDto:
3346
+ type: object
3347
+ required: [lastAttemptAt, counts]
3348
+ properties:
3349
+ lastAttemptAt:
3350
+ type: string
3351
+ format: date-time
3352
+ lastSuccessAt:
3353
+ type: string
3354
+ format: date-time
3355
+ nullable: true
3356
+ lastErrorKind:
3357
+ type: string
3358
+ nullable: true
3359
+ lastErrorMessage:
3360
+ type: string
3361
+ nullable: true
3362
+ durationMs:
3363
+ type: integer
3364
+ format: int64
3365
+ nullable: true
3366
+ counts:
3367
+ type: object
3368
+ additionalProperties: true
3369
+
3370
+ AiToolRefreshResponseDto:
3371
+ type: object
3372
+ required: [ok]
3373
+ properties:
3374
+ ok:
3375
+ type: boolean
3376
+ error:
3377
+ type: string
3378
+ nullable: true
3379
+
3380
+ DashboardProductivityMetricsDto:
3381
+ type: object
3382
+ description: Nested metric groups; see TypeScript `DashboardProductivityMetricsDto` in @thinkai/tai-api-contract.
3383
+ additionalProperties: true
3384
+
3385
+ DashboardProductivityDto:
3386
+ type: object
3387
+ required: [workspaceId, windowStart, windowEnd, scope, metrics]
3388
+ properties:
3389
+ workspaceId:
3390
+ type: string
3391
+ format: uuid
3392
+ generatedAt:
3393
+ type: string
3394
+ format: date-time
3395
+ description: Response generation time (RFC 3339).
3396
+ windowStart:
3397
+ type: string
3398
+ format: date-time
3399
+ windowEnd:
3400
+ type: string
3401
+ format: date-time
3402
+ scope:
3403
+ type: string
3404
+ enum: [org, team, person]
3405
+ metrics:
3406
+ $ref: "#/components/schemas/DashboardProductivityMetricsDto"
3407
+ meta:
3408
+ type: object
3409
+ additionalProperties: true
3410
+
3411
+ DashboardProductivityHistoryDto:
3412
+ type: object
3413
+ required: [workspaceId, bucket, buckets]
3414
+ properties:
3415
+ workspaceId:
3416
+ type: string
3417
+ format: uuid
3418
+ bucket:
3419
+ type: string
3420
+ enum: [day, week, month]
3421
+ buckets:
3422
+ type: array
3423
+ items:
3424
+ type: object
3425
+ required: [bucketStart, bucketEnd, metrics]
3426
+ properties:
3427
+ bucketStart:
3428
+ type: string
3429
+ format: date-time
3430
+ bucketEnd:
3431
+ type: string
3432
+ format: date-time
3433
+ metrics:
3434
+ $ref: "#/components/schemas/DashboardProductivityMetricsDto"
3435
+
3436
+ ProductivityInsightsAggregatedTeamsScopeDto:
3437
+ type: object
3438
+ additionalProperties: false
3439
+ required: [department, team]
3440
+ properties:
3441
+ department:
3442
+ type: string
3443
+ description: >
3444
+ Applied department filter from the Measure UI. Backend scopes via CSV import
3445
+ (`thinkai_org_structure`) or SPA `department` nodes; unknown departments yield
3446
+ empty scoped results rather than org-wide rollups.
3447
+ team:
3448
+ type: string
3449
+ description: Applied team name filter from the Measure UI.
3450
+
3451
+ ProductivityInsightsAggregatedTeamWeekDto:
3452
+ type: object
3453
+ additionalProperties: false
3454
+ required: [bucketStart, bucketEnd, mergedPrCount, failRatePercent]
3455
+ properties:
3456
+ bucketStart:
3457
+ type: string
3458
+ format: date-time
3459
+ bucketEnd:
3460
+ type: string
3461
+ format: date-time
3462
+ weekLabel:
3463
+ type: string
3464
+ description: Short label for the week column (MM/DD), aligned with Measure charts.
3465
+ mergedPrCount:
3466
+ type: number
3467
+ failRatePercent:
3468
+ type: number
3469
+
3470
+ ProductivityInsightsAggregatedTeamRowDto:
3471
+ type: object
3472
+ additionalProperties: false
3473
+ required: [teamId, teamName, headcount, weeks]
3474
+ properties:
3475
+ teamId:
3476
+ type: string
3477
+ teamName:
3478
+ type: string
3479
+ headcount:
3480
+ type: integer
3481
+ minimum: 0
3482
+ managerLabel:
3483
+ type: string
3484
+ weeks:
3485
+ type: array
3486
+ items:
3487
+ $ref: "#/components/schemas/ProductivityInsightsAggregatedTeamWeekDto"
3488
+
3489
+ ProductivityInsightsAggregatedTeamsDto:
3490
+ type: object
3491
+ additionalProperties: false
3492
+ required: [workspaceId, range, scope, generatedAt, teams]
3493
+ properties:
3494
+ workspaceId:
3495
+ type: string
3496
+ format: uuid
3497
+ range:
3498
+ type: string
3499
+ enum: [4w, 8w, q]
3500
+ scope:
3501
+ $ref: "#/components/schemas/ProductivityInsightsAggregatedTeamsScopeDto"
3502
+ generatedAt:
3503
+ type: string
3504
+ format: date-time
3505
+ teams:
3506
+ type: array
3507
+ items:
3508
+ $ref: "#/components/schemas/ProductivityInsightsAggregatedTeamRowDto"
3509
+
3510
+ ProductivityInsightsMergedPrTrendWeekDto:
3511
+ type: object
3512
+ additionalProperties: false
3513
+ required: [bucketStart, bucketEnd, mergedPrCount]
3514
+ properties:
3515
+ bucketStart:
3516
+ type: string
3517
+ format: date-time
3518
+ bucketEnd:
3519
+ type: string
3520
+ format: date-time
3521
+ weekLabel:
3522
+ type: string
3523
+ description: Short label for the week column (MM/DD), aligned with Measure charts.
3524
+ mergedPrCount:
3525
+ type: number
3526
+
3527
+ ProductivityInsightsMergedPrContributorRowDto:
3528
+ type: object
3529
+ additionalProperties: false
3530
+ required: [personId, personName, weeks]
3531
+ properties:
3532
+ personId:
3533
+ type: string
3534
+ description: Canonical person identifier (matches org chart person_id / employeeId).
3535
+ personName:
3536
+ type: string
3537
+ teamName:
3538
+ type: string
3539
+ weeks:
3540
+ type: array
3541
+ items:
3542
+ $ref: "#/components/schemas/ProductivityInsightsMergedPrTrendWeekDto"
3543
+
3544
+ ProductivityInsightsMergedPrTrendsDto:
3545
+ type: object
3546
+ additionalProperties: false
3547
+ required: [workspaceId, range, scope, generatedAt, weeks, contributors]
3548
+ properties:
3549
+ workspaceId:
3550
+ type: string
3551
+ format: uuid
3552
+ range:
3553
+ type: string
3554
+ enum: [4w, 8w, q]
3555
+ scope:
3556
+ $ref: "#/components/schemas/ProductivityInsightsAggregatedTeamsScopeDto"
3557
+ generatedAt:
3558
+ type: string
3559
+ format: date-time
3560
+ weeks:
3561
+ type: array
3562
+ description: Portfolio aggregate merged PR counts (sum of filtered contributors per week).
3563
+ items:
3564
+ $ref: "#/components/schemas/ProductivityInsightsMergedPrTrendWeekDto"
3565
+ contributors:
3566
+ type: array
3567
+ items:
3568
+ $ref: "#/components/schemas/ProductivityInsightsMergedPrContributorRowDto"
3569
+
3570
+ ProductivityInsightsTeamLeaderItemDto:
3571
+ type: object
3572
+ additionalProperties: false
3573
+ required:
3574
+ [rank, teamId, teamName, periodAvgMergedPrs, deltaVsPeriodAvgPercent]
3575
+ properties:
3576
+ rank:
3577
+ type: integer
3578
+ minimum: 1
3579
+ description: 1-based rank within the top-teams list (1 = highest performer).
3580
+ teamId:
3581
+ type: string
3582
+ teamName:
3583
+ type: string
3584
+ subtitle:
3585
+ type: string
3586
+ description: Optional context line (e.g. headcount or manager).
3587
+ periodAvgMergedPrs:
3588
+ type: number
3589
+ description: Average weekly merged PR count over the selected range.
3590
+ deltaVsPeriodAvgPercent:
3591
+ type: number
3592
+ description: Percent change of the latest week vs the period average.
3593
+ avgFailRatePercent:
3594
+ type: number
3595
+ description: Average change failure rate over the range when available.
3596
+
3597
+ ProductivityInsightsTeamSummaryDto:
3598
+ type: object
3599
+ additionalProperties: false
3600
+ required: [totalMergedPrs, avgMergedPrsPerWeek, avgFailRatePercent, headcount]
3601
+ properties:
3602
+ totalMergedPrs:
3603
+ type: number
3604
+ avgMergedPrsPerWeek:
3605
+ type: number
3606
+ avgFailRatePercent:
3607
+ type: number
3608
+ headcount:
3609
+ type: integer
3610
+ minimum: 0
2286
3611
 
2287
- schemas:
2288
- ErrorMessageDto:
3612
+ ProductivityInsightsTeamDetailDto:
2289
3613
  type: object
2290
- required: [error]
3614
+ additionalProperties: false
3615
+ required: [teamId, teamName, headcount, weeks, summary]
2291
3616
  properties:
2292
- error:
3617
+ teamId:
3618
+ type: string
3619
+ teamName:
3620
+ type: string
3621
+ headcount:
3622
+ type: integer
3623
+ minimum: 0
3624
+ managerLabel:
2293
3625
  type: string
3626
+ weeks:
3627
+ type: array
3628
+ items:
3629
+ $ref: "#/components/schemas/ProductivityInsightsAggregatedTeamWeekDto"
3630
+ summary:
3631
+ $ref: "#/components/schemas/ProductivityInsightsTeamSummaryDto"
2294
3632
 
2295
- TeamMemberDto:
3633
+ ProductivityInsightsTeamsResponseDto:
2296
3634
  type: object
2297
- required: [id, name, email, role]
3635
+ additionalProperties: false
3636
+ required: [workspaceId, range, scope, generatedAt]
3637
+ properties:
3638
+ workspaceId:
3639
+ type: string
3640
+ format: uuid
3641
+ range:
3642
+ type: string
3643
+ enum: [4w, 8w, q]
3644
+ scope:
3645
+ $ref: "#/components/schemas/ProductivityInsightsAggregatedTeamsScopeDto"
3646
+ generatedAt:
3647
+ type: string
3648
+ format: date-time
3649
+ items:
3650
+ type: array
3651
+ description: Present when `teamId` query is omitted — top-ranked teams.
3652
+ items:
3653
+ $ref: "#/components/schemas/ProductivityInsightsTeamLeaderItemDto"
3654
+ team:
3655
+ description: Present when `teamId` query is set — single-team drilldown.
3656
+ allOf:
3657
+ - $ref: "#/components/schemas/ProductivityInsightsTeamDetailDto"
3658
+
3659
+ ProductivityInsightsContributorProfileDto:
3660
+ type: object
3661
+ additionalProperties: false
3662
+ required: [id, name]
2298
3663
  properties:
2299
3664
  id:
2300
3665
  type: string
3666
+ description: Org-chart person id (typically email).
2301
3667
  name:
2302
3668
  type: string
2303
- email:
3669
+ title:
2304
3670
  type: string
2305
- role:
3671
+ team:
3672
+ type: string
3673
+ department:
2306
3674
  type: string
2307
- enum: [admin, editor, viewer]
2308
3675
 
2309
- TeamMemberListDto:
3676
+ ProductivityInsightsContributorActivityDayDto:
2310
3677
  type: object
2311
- required: [members]
3678
+ additionalProperties: false
3679
+ required:
3680
+ [date, dayIndex, mergedPrCount, mergedPrs, absent, failureSignal, failures]
2312
3681
  properties:
2313
- members:
3682
+ date:
3683
+ type: string
3684
+ format: date
3685
+ dayIndex:
3686
+ type: integer
3687
+ minimum: 0
3688
+ maximum: 6
3689
+ mergedPrCount:
3690
+ type: integer
3691
+ minimum: 0
3692
+ mergedPrs:
2314
3693
  type: array
2315
3694
  items:
2316
- $ref: "#/components/schemas/TeamMemberDto"
3695
+ type: string
3696
+ absent:
3697
+ type: boolean
3698
+ absenceType:
3699
+ type: string
3700
+ failureSignal:
3701
+ type: boolean
3702
+ failures:
3703
+ type: array
3704
+ items:
3705
+ type: string
2317
3706
 
2318
- InviteTeamMemberBodyDto:
3707
+ ProductivityInsightsContributorActivityWeekDto:
2319
3708
  type: object
2320
- required: [email, role]
3709
+ additionalProperties: false
3710
+ required: [label, bucketStart, bucketEnd, days]
2321
3711
  properties:
2322
- email:
3712
+ label:
2323
3713
  type: string
2324
- role:
3714
+ bucketStart:
2325
3715
  type: string
2326
- enum: [admin, editor, viewer]
3716
+ format: date-time
3717
+ bucketEnd:
3718
+ type: string
3719
+ format: date-time
3720
+ days:
3721
+ type: array
3722
+ items:
3723
+ $ref: "#/components/schemas/ProductivityInsightsContributorActivityDayDto"
2327
3724
 
2328
- UpdateTeamMemberRoleBodyDto:
3725
+ ProductivityInsightsContributorActivitySummaryDto:
2329
3726
  type: object
2330
- required: [role]
3727
+ additionalProperties: false
3728
+ required:
3729
+ - totalMergedPrs
3730
+ - avgMergedPrsPerWeek
3731
+ - totalAbsenceDays
3732
+ - avgFailRate
3733
+ - mergedPrDelta
3734
+ - avgMergedPrsPerWeekDelta
3735
+ - totalAbsenceDaysDelta
3736
+ - avgFailRateDelta
2331
3737
  properties:
2332
- role:
2333
- type: string
2334
- enum: [admin, editor, viewer]
3738
+ totalMergedPrs:
3739
+ type: integer
3740
+ minimum: 0
3741
+ avgMergedPrsPerWeek:
3742
+ type: number
3743
+ totalAbsenceDays:
3744
+ type: integer
3745
+ minimum: 0
3746
+ avgFailRate:
3747
+ type: number
3748
+ mergedPrDelta:
3749
+ type: number
3750
+ avgMergedPrsPerWeekDelta:
3751
+ type: number
3752
+ totalAbsenceDaysDelta:
3753
+ type: number
3754
+ avgFailRateDelta:
3755
+ type: number
2335
3756
 
2336
- OrgChartNodeDto:
3757
+ ProductivityInsightsContributorDetailDto:
2337
3758
  type: object
2338
3759
  additionalProperties: false
2339
- required: [id, name, type]
3760
+ required:
3761
+ - workspaceId
3762
+ - contributorId
3763
+ - range
3764
+ - scope
3765
+ - generatedAt
3766
+ - contributor
3767
+ - weeks
3768
+ - summary
2340
3769
  properties:
2341
- id:
3770
+ workspaceId:
2342
3771
  type: string
2343
- name:
3772
+ format: uuid
3773
+ contributorId:
2344
3774
  type: string
2345
- type:
3775
+ range:
2346
3776
  type: string
2347
- enum: [org, department, team, person]
2348
- external_ids:
2349
- type: object
2350
- additionalProperties: { type: string }
2351
- children:
3777
+ enum: [4w, 8w, q]
3778
+ scope:
3779
+ $ref: "#/components/schemas/ProductivityInsightsAggregatedTeamsScopeDto"
3780
+ generatedAt:
3781
+ type: string
3782
+ format: date-time
3783
+ contributor:
3784
+ $ref: "#/components/schemas/ProductivityInsightsContributorProfileDto"
3785
+ weeks:
2352
3786
  type: array
2353
3787
  items:
2354
- $ref: "#/components/schemas/OrgChartNodeDto"
3788
+ $ref: "#/components/schemas/ProductivityInsightsContributorActivityWeekDto"
3789
+ summary:
3790
+ $ref: "#/components/schemas/ProductivityInsightsContributorActivitySummaryDto"
2355
3791
 
2356
- OrgChartTreeNodeDto:
3792
+ ProductivityInsightsNeedsAttentionItemDto:
2357
3793
  type: object
2358
- required: [id]
3794
+ additionalProperties: false
3795
+ required:
3796
+ [rank, entityId, entityType, name, periodAvgMergedPrs, deltaVsPeriodAvgPercent]
2359
3797
  properties:
2360
- id:
3798
+ rank:
3799
+ type: integer
3800
+ minimum: 1
3801
+ description: 1-based rank within the laggards list (1 = lowest performer).
3802
+ entityId:
3803
+ type: string
3804
+ description: Org chart team id or canonical person id.
3805
+ entityType:
2361
3806
  type: string
3807
+ enum: [team, contributor]
2362
3808
  name:
2363
3809
  type: string
2364
- type:
3810
+ subtitle:
2365
3811
  type: string
2366
- person_id:
3812
+ description: Optional context line (e.g. headcount or job title).
3813
+ periodAvgMergedPrs:
3814
+ type: number
3815
+ description: Mean weekly merged PR count across the selected range.
3816
+ deltaVsPeriodAvgPercent:
3817
+ type: number
3818
+ description: Percent change of the latest week vs the period mean.
3819
+ avgFailRatePercent:
3820
+ type: number
3821
+ description: Mean weekly change-failure rate when available.
3822
+
3823
+ ProductivityInsightsNeedsAttentionDto:
3824
+ type: object
3825
+ additionalProperties: false
3826
+ required: [workspaceId, range, scope, entityType, generatedAt, items]
3827
+ properties:
3828
+ workspaceId:
2367
3829
  type: string
2368
- team:
3830
+ format: uuid
3831
+ range:
2369
3832
  type: string
2370
- role:
3833
+ enum: [4w, 8w, q]
3834
+ scope:
3835
+ $ref: "#/components/schemas/ProductivityInsightsAggregatedTeamsScopeDto"
3836
+ entityType:
2371
3837
  type: string
2372
- level:
3838
+ enum: [teams, contributors]
3839
+ generatedAt:
2373
3840
  type: string
2374
- external_ids:
2375
- type: object
2376
- additionalProperties: { type: string }
2377
- children:
3841
+ format: date-time
3842
+ items:
2378
3843
  type: array
2379
3844
  items:
2380
- $ref: "#/components/schemas/OrgChartTreeNodeDto"
3845
+ $ref: "#/components/schemas/ProductivityInsightsNeedsAttentionItemDto"
2381
3846
 
2382
- ScoringOrgChartDto:
3847
+ AiUsageSpendAggregatedTeamWeekDto:
2383
3848
  type: object
2384
3849
  additionalProperties: false
2385
- required: [root]
3850
+ required: [bucketStart, bucketEnd, tokenCount, spendCents, activeUsers]
2386
3851
  properties:
2387
- root:
2388
- $ref: "#/components/schemas/OrgChartTreeNodeDto"
3852
+ bucketStart:
3853
+ type: string
3854
+ format: date-time
3855
+ bucketEnd:
3856
+ type: string
3857
+ format: date-time
3858
+ weekLabel:
3859
+ type: string
3860
+ description: Short label for table headers (e.g. MM/DD).
3861
+ tokenCount:
3862
+ type: integer
3863
+ minimum: 0
3864
+ description: Total tokens consumed in the bucket (from AI-tool daily usage payloads when available).
3865
+ spendCents:
3866
+ type: integer
3867
+ minimum: 0
3868
+ description: Estimated spend in USD cents attributed to the bucket.
3869
+ activeUsers:
3870
+ type: integer
3871
+ minimum: 0
3872
+ description: Distinct engineers with active AI-tool usage in the bucket.
2389
3873
 
2390
- OrgChartDto:
3874
+ AiUsageSpendAggregatedTeamRowDto:
2391
3875
  type: object
2392
3876
  additionalProperties: false
2393
- required: [root]
3877
+ required: [teamId, teamName, headcount, weeks]
2394
3878
  properties:
2395
- root:
2396
- $ref: "#/components/schemas/OrgChartNodeDto"
3879
+ teamId:
3880
+ type: string
3881
+ teamName:
3882
+ type: string
3883
+ headcount:
3884
+ type: integer
3885
+ minimum: 0
3886
+ managerLabel:
3887
+ type: string
3888
+ weeks:
3889
+ type: array
3890
+ items:
3891
+ $ref: "#/components/schemas/AiUsageSpendAggregatedTeamWeekDto"
2397
3892
 
2398
- OrgChartPersonDto:
3893
+ AiUsageSpendAggregatedTeamsSummaryDto:
2399
3894
  type: object
2400
3895
  additionalProperties: false
2401
- required: [employeeId, name, email, title, department, team, managerId, location]
3896
+ required:
3897
+ [totalTokenCount, totalSpendCents, activeUsers, activeTools, totalEngineers]
2402
3898
  properties:
2403
- employeeId:
3899
+ totalTokenCount:
3900
+ type: integer
3901
+ minimum: 0
3902
+ totalSpendCents:
3903
+ type: integer
3904
+ minimum: 0
3905
+ activeUsers:
3906
+ type: integer
3907
+ minimum: 0
3908
+ description: Distinct engineers with any active usage in the selected range.
3909
+ activeTools:
3910
+ type: integer
3911
+ minimum: 0
3912
+ description: Count of connected AI-tool providers with usage or spend in range.
3913
+ totalEngineers:
3914
+ type: integer
3915
+ minimum: 0
3916
+ description: Sum of org-chart headcount across returned teams.
3917
+
3918
+ AiUsageSpendAggregatedTeamsDto:
3919
+ type: object
3920
+ additionalProperties: false
3921
+ required: [workspaceId, range, scope, generatedAt, teams, summary]
3922
+ properties:
3923
+ workspaceId:
2404
3924
  type: string
2405
- name:
3925
+ format: uuid
3926
+ range:
2406
3927
  type: string
2407
- email:
3928
+ enum: [4w, 8w, q]
3929
+ scope:
3930
+ $ref: "#/components/schemas/ProductivityInsightsAggregatedTeamsScopeDto"
3931
+ generatedAt:
2408
3932
  type: string
2409
- title:
3933
+ format: date-time
3934
+ teams:
3935
+ type: array
3936
+ items:
3937
+ $ref: "#/components/schemas/AiUsageSpendAggregatedTeamRowDto"
3938
+ summary:
3939
+ $ref: "#/components/schemas/AiUsageSpendAggregatedTeamsSummaryDto"
3940
+
3941
+ AiUsageSpendAggregatedContributorRowDto:
3942
+ type: object
3943
+ additionalProperties: false
3944
+ required: [contributorId, contributorName, weeks]
3945
+ properties:
3946
+ contributorId:
2410
3947
  type: string
2411
- department:
3948
+ description: Org-chart person id (typically email).
3949
+ contributorName:
2412
3950
  type: string
2413
- team:
3951
+ title:
2414
3952
  type: string
2415
- managerId:
3953
+ team:
2416
3954
  type: string
2417
- location:
3955
+ department:
2418
3956
  type: string
3957
+ weeks:
3958
+ type: array
3959
+ items:
3960
+ $ref: "#/components/schemas/AiUsageSpendAggregatedTeamWeekDto"
2419
3961
 
2420
- OrgChartImportResultDto:
3962
+ AiUsageSpendAggregatedContributorsDto:
2421
3963
  type: object
2422
3964
  additionalProperties: false
2423
- required: [persons, errors]
3965
+ required: [workspaceId, range, scope, generatedAt, contributors, summary]
2424
3966
  properties:
2425
- persons:
2426
- type: array
2427
- items:
2428
- $ref: "#/components/schemas/OrgChartPersonDto"
2429
- errors:
3967
+ workspaceId:
3968
+ type: string
3969
+ format: uuid
3970
+ range:
3971
+ type: string
3972
+ enum: [4w, 8w, q]
3973
+ scope:
3974
+ $ref: "#/components/schemas/ProductivityInsightsAggregatedTeamsScopeDto"
3975
+ generatedAt:
3976
+ type: string
3977
+ format: date-time
3978
+ contributors:
2430
3979
  type: array
2431
3980
  items:
2432
- type: string
3981
+ $ref: "#/components/schemas/AiUsageSpendAggregatedContributorRowDto"
3982
+ summary:
3983
+ $ref: "#/components/schemas/AiUsageSpendAggregatedTeamsSummaryDto"
2433
3984
 
2434
- OrgChartSaveResultDto:
3985
+ AiUsageSpendContributorToolHeatmapDto:
2435
3986
  type: object
2436
3987
  additionalProperties: false
2437
- required: [ok, tenantId]
3988
+ required:
3989
+ - workspaceId
3990
+ - range
3991
+ - scope
3992
+ - generatedAt
3993
+ - contributors
2438
3994
  properties:
2439
- ok:
2440
- type: boolean
2441
- tenantId:
3995
+ workspaceId:
2442
3996
  type: string
2443
3997
  format: uuid
2444
-
2445
- TenantSourceEntryDto:
2446
- type: object
2447
- required: [type]
2448
- properties:
2449
- type:
3998
+ range:
2450
3999
  type: string
2451
- additionalProperties: true
4000
+ enum: [4w, 8w, q]
4001
+ scope:
4002
+ $ref: "#/components/schemas/ProductivityInsightsAggregatedTeamsScopeDto"
4003
+ generatedAt:
4004
+ type: string
4005
+ format: date-time
4006
+ contributors:
4007
+ type: array
4008
+ items:
4009
+ $ref: "#/components/schemas/AiUsageSpendAggregatedContributorRowDto"
2452
4010
 
2453
- CursorSourceDto:
4011
+ AiUsageSpendKeyInsightItemDto:
2454
4012
  type: object
2455
- description: >
2456
- Cursor (AI Tools) source entry for `PUT /workspaces/{workspaceId}/sources`
2457
- and `POST /workspaces/{workspaceId}/sources/test`. The `token` field
2458
- carries a literal Cursor API key on writes; reads from
2459
- `GET /workspaces/{workspaceId}/config` redact it to `***`.
2460
- Runtime calls use the Cursor Admin API at `https://api.cursor.com` (HTTP Basic); do not rely on `baseUrl`.
2461
- required: [type, token]
2462
4013
  additionalProperties: false
4014
+ required: [id, tone, text]
2463
4015
  properties:
2464
- type:
4016
+ id:
2465
4017
  type: string
2466
- enum: [cursor]
2467
- token:
4018
+ description: Stable insight key (e.g. spend-leader, spend-vs-output).
4019
+ tone:
2468
4020
  type: string
2469
- minLength: 1
2470
- maxLength: 256
2471
- description: Cursor API key (literal). Whitespace and `env:` prefix are rejected.
2472
- baseUrl:
4021
+ enum: [positive, warning, neutral]
4022
+ text:
2473
4023
  type: string
2474
- deprecated: true
2475
- description: >
2476
- Deprecated. Historically suggested an API base URL; the server ignores this field
2477
- and always uses the Cursor Admin API host. Kept so older clients can submit payloads unchanged.
2478
- default: "https://api.cursor.com"
4024
+ description: Human-readable insight sentence for the UI panel.
2479
4025
 
2480
- WorkspaceConfigDto:
4026
+ AiUsageSpendKeyInsightsDto:
2481
4027
  type: object
2482
- required: [sources]
4028
+ additionalProperties: false
4029
+ required: [workspaceId, range, scope, generatedAt, items]
2483
4030
  properties:
2484
- sources:
4031
+ workspaceId:
4032
+ type: string
4033
+ format: uuid
4034
+ range:
4035
+ type: string
4036
+ enum: [4w, 8w, q]
4037
+ scope:
4038
+ $ref: "#/components/schemas/ProductivityInsightsAggregatedTeamsScopeDto"
4039
+ generatedAt:
4040
+ type: string
4041
+ format: date-time
4042
+ items:
2485
4043
  type: array
2486
4044
  items:
2487
- $ref: "#/components/schemas/TenantSourceEntryDto"
2488
- orgChart:
2489
- nullable: true
2490
- allOf:
2491
- - $ref: "#/components/schemas/ScoringOrgChartDto"
2492
- region:
2493
- type: string
2494
- nullable: true
2495
- enum: [us, eu, me]
4045
+ $ref: "#/components/schemas/AiUsageSpendKeyInsightItemDto"
2496
4046
 
2497
- TestConnectionResponseDto:
2498
- type: object
2499
- required: [success]
2500
- properties:
2501
- success:
2502
- type: boolean
2503
- error:
2504
- type: string
4047
+ AiAssistedCodeDataQuality:
4048
+ type: string
4049
+ enum: [estimated, measured]
4050
+ description: >
4051
+ **measured** — derived from scoped productivity ingest
4052
+ (`aiAdoption.aiAssistedContributionRatioPercent`). **estimated** — derived from
4053
+ AI-tool token volume heuristics when ingest ratios are absent for that bucket.
2505
4054
 
2506
- AiToolSeatDto:
4055
+ AiUsageSpendAiAssistedCodeWeekDto:
2507
4056
  type: object
2508
- required: [externalId, email, isRemoved]
4057
+ additionalProperties: false
4058
+ required:
4059
+ [
4060
+ bucketStart,
4061
+ bucketEnd,
4062
+ aiAssistedCodePercent,
4063
+ totalLoc,
4064
+ aiLoc,
4065
+ humanLoc,
4066
+ dataQuality,
4067
+ ]
2509
4068
  properties:
2510
- externalId:
4069
+ bucketStart:
2511
4070
  type: string
2512
- email:
2513
- type: string
2514
- displayName:
4071
+ format: date-time
4072
+ bucketEnd:
2515
4073
  type: string
2516
- nullable: true
2517
- role:
4074
+ format: date-time
4075
+ weekLabel:
2518
4076
  type: string
2519
- nullable: true
2520
- isRemoved:
2521
- type: boolean
2522
- payload:
2523
- type: object
2524
- additionalProperties: true
4077
+ aiAssistedCodePercent:
4078
+ type: number
4079
+ minimum: 0
4080
+ maximum: 100
4081
+ totalLoc:
4082
+ type: integer
4083
+ minimum: 0
4084
+ aiLoc:
4085
+ type: integer
4086
+ minimum: 0
4087
+ humanLoc:
4088
+ type: integer
4089
+ minimum: 0
4090
+ dataQuality:
4091
+ $ref: "#/components/schemas/AiAssistedCodeDataQuality"
2525
4092
 
2526
- AiToolDailyUsageRowDto:
4093
+ AiUsageSpendAiAssistedCodePercentageDto:
2527
4094
  type: object
2528
- required: [day, externalId, isActive]
4095
+ additionalProperties: false
4096
+ required:
4097
+ - workspaceId
4098
+ - range
4099
+ - scope
4100
+ - generatedAt
4101
+ - aiAssistedCodePercent
4102
+ - deltaVsPriorPeriodPercent
4103
+ - totalLoc
4104
+ - aiLoc
4105
+ - humanLoc
4106
+ - dataQuality
4107
+ - weeks
2529
4108
  properties:
2530
- day:
2531
- type: string
2532
- format: date
2533
- externalId:
4109
+ workspaceId:
2534
4110
  type: string
2535
- email:
4111
+ format: uuid
4112
+ range:
2536
4113
  type: string
2537
- nullable: true
2538
- isActive:
2539
- type: boolean
2540
- mostUsedModel:
4114
+ enum: [4w, 8w, q]
4115
+ scope:
4116
+ $ref: "#/components/schemas/ProductivityInsightsAggregatedTeamsScopeDto"
4117
+ generatedAt:
2541
4118
  type: string
2542
- nullable: true
2543
- payload:
2544
- type: object
2545
- additionalProperties: true
4119
+ format: date-time
4120
+ aiAssistedCodePercent:
4121
+ type: number
4122
+ minimum: 0
4123
+ maximum: 100
4124
+ description: Portfolio AI-assisted code share for the selected period (0–100).
4125
+ dataQuality:
4126
+ description: >
4127
+ Period rollup quality. **measured** only when every weekly bucket in `weeks`
4128
+ used ingest ratios; otherwise **estimated** (including mixed ingest/heuristic weeks).
4129
+ allOf:
4130
+ - $ref: "#/components/schemas/AiAssistedCodeDataQuality"
4131
+ deltaVsPriorPeriodPercent:
4132
+ type: number
4133
+ description: Change in AI-assisted code share vs the prior half of the range (percentage points).
4134
+ totalLoc:
4135
+ type: integer
4136
+ minimum: 0
4137
+ aiLoc:
4138
+ type: integer
4139
+ minimum: 0
4140
+ humanLoc:
4141
+ type: integer
4142
+ minimum: 0
4143
+ targetMinPercent:
4144
+ type: number
4145
+ default: 50
4146
+ targetMaxPercent:
4147
+ type: number
4148
+ default: 75
4149
+ weeks:
4150
+ type: array
4151
+ items:
4152
+ $ref: "#/components/schemas/AiUsageSpendAiAssistedCodeWeekDto"
2546
4153
 
2547
- AiToolSpendRowDto:
4154
+ AiUsageSpendAiToolUsageSpendProviderItemDto:
2548
4155
  type: object
2549
- required: [billingCycleStartMs, externalId, spendCents]
4156
+ additionalProperties: false
4157
+ required: [providerId, providerName, tokenCount, spendCents, activeUsers]
2550
4158
  properties:
2551
- billingCycleStartMs:
2552
- type: integer
2553
- format: int64
2554
- externalId:
4159
+ providerId:
2555
4160
  type: string
2556
- email:
4161
+ providerName:
2557
4162
  type: string
2558
- nullable: true
4163
+ tokenCount:
4164
+ type: integer
4165
+ minimum: 0
2559
4166
  spendCents:
2560
4167
  type: integer
2561
- format: int64
2562
- payload:
2563
- type: object
2564
- additionalProperties: true
4168
+ minimum: 0
4169
+ activeUsers:
4170
+ type: integer
4171
+ minimum: 0
2565
4172
 
2566
- AiToolRefreshStatusDto:
4173
+ AiUsageSpendAiToolUsageSpendModelItemDto:
2567
4174
  type: object
2568
- required: [lastAttemptAt, counts]
4175
+ additionalProperties: false
4176
+ required: [providerId, providerName, modelName, tokenCount, spendCents]
2569
4177
  properties:
2570
- lastAttemptAt:
2571
- type: string
2572
- format: date-time
2573
- lastSuccessAt:
4178
+ providerId:
2574
4179
  type: string
2575
- format: date-time
2576
- nullable: true
2577
- lastErrorKind:
4180
+ providerName:
2578
4181
  type: string
2579
- nullable: true
2580
- lastErrorMessage:
4182
+ modelName:
2581
4183
  type: string
2582
- nullable: true
2583
- durationMs:
4184
+ tokenCount:
2584
4185
  type: integer
2585
- format: int64
2586
- nullable: true
2587
- counts:
2588
- type: object
2589
- additionalProperties: true
4186
+ minimum: 0
4187
+ spendCents:
4188
+ type: integer
4189
+ minimum: 0
2590
4190
 
2591
- AiToolRefreshResponseDto:
4191
+ AiUsageSpendAiToolUsageSpendDto:
2592
4192
  type: object
2593
- required: [ok]
4193
+ additionalProperties: false
4194
+ required:
4195
+ - workspaceId
4196
+ - range
4197
+ - scope
4198
+ - generatedAt
4199
+ - totalEngineers
4200
+ - providers
4201
+ - models
2594
4202
  properties:
2595
- ok:
2596
- type: boolean
2597
- error:
4203
+ workspaceId:
2598
4204
  type: string
2599
- nullable: true
2600
-
2601
- DashboardProductivityMetricsDto:
2602
- type: object
2603
- description: Nested metric groups; see TypeScript `DashboardProductivityMetricsDto` in @thinkai/tai-api-contract.
2604
- additionalProperties: true
4205
+ format: uuid
4206
+ range:
4207
+ type: string
4208
+ enum: [4w, 8w, q]
4209
+ scope:
4210
+ $ref: "#/components/schemas/ProductivityInsightsAggregatedTeamsScopeDto"
4211
+ generatedAt:
4212
+ type: string
4213
+ format: date-time
4214
+ totalEngineers:
4215
+ type: integer
4216
+ minimum: 0
4217
+ providers:
4218
+ type: array
4219
+ items:
4220
+ $ref: "#/components/schemas/AiUsageSpendAiToolUsageSpendProviderItemDto"
4221
+ models:
4222
+ type: array
4223
+ items:
4224
+ $ref: "#/components/schemas/AiUsageSpendAiToolUsageSpendModelItemDto"
2605
4225
 
2606
- DashboardProductivityDto:
4226
+ AiUsageSpendTeamToolHeatmapDto:
2607
4227
  type: object
2608
- required: [workspaceId, windowStart, windowEnd, scope, metrics]
4228
+ additionalProperties: false
4229
+ required:
4230
+ - workspaceId
4231
+ - range
4232
+ - scope
4233
+ - generatedAt
4234
+ - teams
2609
4235
  properties:
2610
4236
  workspaceId:
2611
4237
  type: string
2612
4238
  format: uuid
4239
+ range:
4240
+ type: string
4241
+ enum: [4w, 8w, q]
4242
+ scope:
4243
+ $ref: "#/components/schemas/ProductivityInsightsAggregatedTeamsScopeDto"
2613
4244
  generatedAt:
2614
4245
  type: string
2615
4246
  format: date-time
2616
- description: Response generation time (RFC 3339).
2617
- windowStart:
4247
+ teams:
4248
+ type: array
4249
+ items:
4250
+ $ref: "#/components/schemas/AiUsageSpendAggregatedTeamRowDto"
4251
+
4252
+ AiUsageSpendRoiProductivitySignalWeekDto:
4253
+ type: object
4254
+ additionalProperties: false
4255
+ required: [bucketStart, bucketEnd, mergedPrCount, tokenCount]
4256
+ properties:
4257
+ bucketStart:
2618
4258
  type: string
2619
4259
  format: date-time
2620
- windowEnd:
4260
+ bucketEnd:
2621
4261
  type: string
2622
4262
  format: date-time
2623
- scope:
4263
+ weekLabel:
2624
4264
  type: string
2625
- enum: [org, team, person]
2626
- metrics:
2627
- $ref: "#/components/schemas/DashboardProductivityMetricsDto"
2628
- meta:
2629
- type: object
2630
- additionalProperties: true
4265
+ mergedPrCount:
4266
+ type: integer
4267
+ minimum: 0
4268
+ description: Estimated merged PR count in the bucket from dashboard velocity metrics.
4269
+ tokenCount:
4270
+ type: integer
4271
+ minimum: 0
4272
+ description: Total AI-tool tokens consumed in the bucket.
2631
4273
 
2632
- DashboardProductivityHistoryDto:
4274
+ AiUsageSpendRoiProductivitySignalDto:
2633
4275
  type: object
2634
- required: [workspaceId, bucket, buckets]
4276
+ additionalProperties: false
4277
+ required:
4278
+ - workspaceId
4279
+ - range
4280
+ - scope
4281
+ - generatedAt
4282
+ - totalMergedPrs
4283
+ - totalSpendCents
4284
+ - activeUsers
4285
+ - totalEngineers
4286
+ - aiAssistedPrRatio
4287
+ - prThroughputDeltaPercent
4288
+ - usageDeltaPercent
4289
+ - weeks
2635
4290
  properties:
2636
4291
  workspaceId:
2637
4292
  type: string
2638
4293
  format: uuid
2639
- bucket:
4294
+ range:
2640
4295
  type: string
2641
- enum: [day, week, month]
2642
- buckets:
4296
+ enum: [4w, 8w, q]
4297
+ scope:
4298
+ $ref: "#/components/schemas/ProductivityInsightsAggregatedTeamsScopeDto"
4299
+ generatedAt:
4300
+ type: string
4301
+ format: date-time
4302
+ totalMergedPrs:
4303
+ type: integer
4304
+ minimum: 0
4305
+ totalSpendCents:
4306
+ type: integer
4307
+ minimum: 0
4308
+ activeUsers:
4309
+ type: integer
4310
+ minimum: 0
4311
+ totalEngineers:
4312
+ type: integer
4313
+ minimum: 0
4314
+ aiAssistedPrRatio:
4315
+ type: number
4316
+ minimum: 0
4317
+ maximum: 1
4318
+ description: Share of merged PRs estimated to be AI-assisted (0–1).
4319
+ prThroughputDeltaPercent:
4320
+ type: number
4321
+ description: Percent change in merged PR throughput (recent half vs prior half of range).
4322
+ usageDeltaPercent:
4323
+ type: number
4324
+ description: Percent change in token usage (recent half vs prior half of range).
4325
+ weeks:
2643
4326
  type: array
2644
4327
  items:
2645
- type: object
2646
- required: [bucketStart, bucketEnd, metrics]
2647
- properties:
2648
- bucketStart:
2649
- type: string
2650
- format: date-time
2651
- bucketEnd:
2652
- type: string
2653
- format: date-time
2654
- metrics:
2655
- $ref: "#/components/schemas/DashboardProductivityMetricsDto"
4328
+ $ref: "#/components/schemas/AiUsageSpendRoiProductivitySignalWeekDto"
2656
4329
 
2657
4330
  MeProfileDto:
2658
4331
  type: object
@@ -2772,9 +4445,26 @@ components:
2772
4445
  type: boolean
2773
4446
  nullable: true
2774
4447
 
4448
+ GithubMissingPermissionDto:
4449
+ type: object
4450
+ additionalProperties: false
4451
+ required: [name, required]
4452
+ properties:
4453
+ name:
4454
+ type: string
4455
+ description: GitHub App permission key (e.g. contents, pull_requests).
4456
+ required:
4457
+ type: string
4458
+ enum: [read, write, admin]
4459
+ granted:
4460
+ type: string
4461
+ enum: [read, write, admin]
4462
+ nullable: true
4463
+ description: Null when the installation has not granted this permission.
4464
+
2775
4465
  GithubInstallationSummaryDto:
2776
4466
  type: object
2777
- required: [installationId, account, repos]
4467
+ required: [installationId, account, repos, permissionsUpgradeRequired]
2778
4468
  properties:
2779
4469
  installationId:
2780
4470
  type: string
@@ -2793,10 +4483,23 @@ components:
2793
4483
  type: string
2794
4484
  format: date-time
2795
4485
  nullable: true
4486
+ permissionsUpgradeRequired:
4487
+ type: boolean
4488
+ description: >
4489
+ True when this installation's granted permissions are below what the ThinkAI GitHub App
4490
+ currently requests.
4491
+ missingPermissions:
4492
+ type: array
4493
+ items:
4494
+ $ref: "#/components/schemas/GithubMissingPermissionDto"
4495
+ upgradeMessage:
4496
+ type: string
4497
+ nullable: true
4498
+ description: Human-readable summary for UI when permissionsUpgradeRequired is true.
2796
4499
 
2797
4500
  GithubInstallationStatusDto:
2798
4501
  type: object
2799
- required: [installed, installations]
4502
+ required: [installed, installations, permissionsUpgradeRequired]
2800
4503
  properties:
2801
4504
  installed:
2802
4505
  type: boolean
@@ -2804,6 +4507,9 @@ components:
2804
4507
  type: array
2805
4508
  items:
2806
4509
  $ref: "#/components/schemas/GithubInstallationSummaryDto"
4510
+ permissionsUpgradeRequired:
4511
+ type: boolean
4512
+ description: True if any connected installation is under-permissioned.
2807
4513
  installationId:
2808
4514
  type: string
2809
4515
  nullable: true