@thinkai/tai-api-contract 2.5.2 → 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,596 +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
675
- - name: permissionsRefresh
701
+ type: string
702
+ - name: limit
676
703
  in: query
677
- required: false
678
- description: >
679
- When true, re-fetch GitHub App and installation permission metadata (used by the
680
- login permission gate after the user approves on GitHub).
704
+ description: Maximum top-team rows when `teamId` is omitted (UI default 3).
681
705
  schema:
682
- type: boolean
683
- default: false
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
684
730
  responses:
685
731
  "200":
686
- description: Installation status
732
+ description: Top teams list or single-team drilldown
687
733
  content:
688
734
  application/json:
689
735
  schema:
690
- $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 }
691
746
  "401":
692
747
  $ref: "#/components/responses/Unauthorized"
693
748
  "403":
694
749
  $ref: "#/components/responses/Forbidden"
695
750
  "404":
696
- description: Workspace does not exist or malformed workspaceId
697
- content:
698
- application/json:
699
- schema:
700
- $ref: "#/components/schemas/ErrorMessageDto"
751
+ description: Workspace or team not found
701
752
 
702
- delete:
703
- tags: [Integrations]
704
- summary: Disconnect GitHub App installation for workspace
705
- operationId: deleteWorkspaceGithubInstallation
753
+ /workspaces/{workspaceId}/insights/productivity-insights/contributors/{contributorId}:
754
+ get:
755
+ tags: [Workspace]
756
+ summary: Productivity insights — contributor drilldown
706
757
  description: >
707
- Removes the workspace GitHub App integration row (and cascaded VCS connection + repos cache).
708
- Idempotent: returns 204 when no installation exists. Does not uninstall the app on GitHub;
709
- use the manage URL or GitHub settings to revoke the app there if required.
710
- security:
711
- - 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
712
763
  parameters:
713
764
  - $ref: "#/components/parameters/WorkspaceId"
714
- - 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
715
772
  in: query
716
- required: false
717
- description: Optional installation id to disconnect a single linked installation.
773
+ description: Measure time range (maps to weekly bucket count).
718
774
  schema:
719
775
  type: string
720
- - name: all
776
+ enum: [4w, 8w, q]
777
+ default: 4w
778
+ - $ref: "#/components/parameters/InsightsDepartmentFilter"
779
+ - name: team
721
780
  in: query
722
- required: false
723
- description: When true, disconnect all linked GitHub installations for this workspace.
781
+ description: Team name filter; send `All teams` to include every team.
724
782
  schema:
725
- 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
726
791
  responses:
727
- "204":
728
- 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 }
729
807
  "401":
730
808
  $ref: "#/components/responses/Unauthorized"
731
809
  "403":
732
810
  $ref: "#/components/responses/Forbidden"
733
811
  "404":
734
- description: Workspace does not exist or malformed workspaceId
735
- content:
736
- application/json:
737
- schema:
738
- $ref: "#/components/schemas/ErrorMessageDto"
812
+ description: Workspace or contributor not found
739
813
 
740
- /workspaces/{workspaceId}/integrations/github/install-url:
741
- post:
742
- tags: [Integrations]
743
- summary: Create GitHub App installation URL
744
- operationId: postGithubInstallUrl
814
+ /workspaces/{workspaceId}/insights/productivity-insights/needs-attention:
815
+ get:
816
+ tags: [Workspace]
817
+ summary: Productivity insights needs attention (bottom performers)
745
818
  description: >
746
- Creates a GitHub App installation URL scoped to the workspace and caller.
747
- Body is optional; when omitted, defaults to `/phase-a/p3` return path.
748
- security:
749
- - 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
750
823
  parameters:
751
824
  - $ref: "#/components/parameters/WorkspaceId"
752
- requestBody:
753
- required: false
754
- content:
755
- application/json:
756
- schema:
757
- $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
758
860
  responses:
759
861
  "200":
760
- description: Installation URL + signed state
862
+ description: Bottom performers for the needs-attention widget
761
863
  content:
762
864
  application/json:
763
865
  schema:
764
- $ref: "#/components/schemas/GithubInstallUrlResponseDto"
866
+ $ref: "#/components/schemas/ProductivityInsightsNeedsAttentionDto"
765
867
  "400":
766
- description: Request validation failed
868
+ description: Invalid query parameters
767
869
  content:
768
870
  application/json:
769
871
  schema:
770
- $ref: "#/components/schemas/ErrorMessageDto"
872
+ type: object
873
+ required: [error]
874
+ properties:
875
+ error: { type: string, example: invalid_query }
771
876
  "401":
772
877
  $ref: "#/components/responses/Unauthorized"
773
878
  "403":
774
879
  $ref: "#/components/responses/Forbidden"
775
880
  "404":
776
881
  description: Workspace does not exist or malformed workspaceId
777
- content:
778
- application/json:
779
- schema:
780
- $ref: "#/components/schemas/ErrorMessageDto"
781
882
 
782
- /workspaces/{workspaceId}/integrations/ai-tool/{provider}/refresh:
783
- post:
784
- tags: [Integrations]
785
- summary: Refresh AI tool integration snapshots
786
- 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
787
887
  description: >
788
- Provider-agnostic refresh. Returns 409 when an advisory-lock refresh is already in progress.
789
- 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
790
893
  parameters:
791
894
  - $ref: "#/components/parameters/WorkspaceId"
792
- - name: provider
793
- in: path
794
- required: true
895
+ - name: rangeId
896
+ in: query
897
+ description: Measure time range (maps to weekly bucket count).
795
898
  schema:
796
899
  type: string
797
- 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
798
915
  responses:
799
916
  "200":
800
- description: Refresh result
917
+ description: Aggregated team AI usage and spend series
801
918
  content:
802
919
  application/json:
803
920
  schema:
804
- $ref: "#/components/schemas/AiToolRefreshResponseDto"
805
- examples:
806
- ok:
807
- value: { ok: true }
808
- invalid_token:
809
- 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 }
810
931
  "401":
811
932
  $ref: "#/components/responses/Unauthorized"
812
933
  "403":
813
934
  $ref: "#/components/responses/Forbidden"
814
935
  "404":
815
- description: Provider source missing for this workspace
816
- content:
817
- application/json:
818
- schema:
819
- $ref: "#/components/schemas/AiToolRefreshResponseDto"
820
- example:
821
- ok: false
822
- error: no_source
823
- "409":
824
- description: Refresh already in progress
825
- content:
826
- application/json:
827
- schema:
828
- $ref: "#/components/schemas/AiToolRefreshResponseDto"
829
- example:
830
- ok: false
831
- error: already_in_progress
936
+ description: Workspace does not exist or malformed workspaceId
832
937
 
833
- /workspaces/{workspaceId}/integrations/ai-tool/{provider}/seats:
938
+ /workspaces/{workspaceId}/insights/ai-usage-spend/aggregated-contributors:
834
939
  get:
835
- tags: [Integrations]
836
- summary: List latest AI-tool seats
837
- 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
838
948
  parameters:
839
949
  - $ref: "#/components/parameters/WorkspaceId"
840
- - name: provider
841
- in: path
842
- required: true
950
+ - name: rangeId
951
+ in: query
952
+ description: Measure time range (maps to weekly bucket count).
843
953
  schema:
844
954
  type: string
845
- 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
846
970
  responses:
847
971
  "200":
848
- 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
849
979
  content:
850
980
  application/json:
851
981
  schema:
852
982
  type: object
853
- required: [seats]
983
+ required: [error]
854
984
  properties:
855
- seats:
856
- type: array
857
- items:
858
- $ref: "#/components/schemas/AiToolSeatDto"
985
+ error: { type: string, example: invalid_query }
859
986
  "401":
860
987
  $ref: "#/components/responses/Unauthorized"
861
988
  "403":
862
989
  $ref: "#/components/responses/Forbidden"
990
+ "404":
991
+ description: Workspace does not exist or malformed workspaceId
863
992
 
864
- /workspaces/{workspaceId}/integrations/ai-tool/{provider}/daily-usage:
993
+ /workspaces/{workspaceId}/insights/ai-usage-spend/contributor-tool-heatmap:
865
994
  get:
866
- tags: [Integrations]
867
- summary: List AI-tool daily usage
868
- 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
869
1002
  parameters:
870
1003
  - $ref: "#/components/parameters/WorkspaceId"
871
- - name: provider
872
- in: path
873
- required: true
874
- schema:
875
- type: string
876
- enum: [cursor]
877
- - name: from
1004
+ - name: rangeId
878
1005
  in: query
879
- required: false
1006
+ description: Measure time range (maps to weekly bucket count).
880
1007
  schema:
881
1008
  type: string
882
- format: date
883
- - name: to
1009
+ enum: [4w, 8w, q]
1010
+ default: 4w
1011
+ - $ref: "#/components/parameters/InsightsDepartmentFilter"
1012
+ - name: team
884
1013
  in: query
885
- required: false
1014
+ description: Team name filter; send `All teams` to include every team.
886
1015
  schema:
887
1016
  type: string
888
- format: date
889
- - name: externalId
1017
+ default: All teams
1018
+ - name: windowEnd
890
1019
  in: query
891
- required: false
1020
+ description: ISO timestamp for the end of the last weekly bucket; omit for current week.
892
1021
  schema:
893
1022
  type: string
1023
+ format: date-time
894
1024
  responses:
895
1025
  "200":
896
- 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
897
1033
  content:
898
1034
  application/json:
899
1035
  schema:
900
1036
  type: object
901
- required: [rows]
1037
+ required: [error]
902
1038
  properties:
903
- rows:
904
- type: array
905
- items:
906
- $ref: "#/components/schemas/AiToolDailyUsageRowDto"
1039
+ error: { type: string, example: invalid_query }
907
1040
  "401":
908
1041
  $ref: "#/components/responses/Unauthorized"
909
1042
  "403":
910
1043
  $ref: "#/components/responses/Forbidden"
1044
+ "404":
1045
+ description: Workspace does not exist or malformed workspaceId
911
1046
 
912
- /workspaces/{workspaceId}/integrations/ai-tool/{provider}/spend:
1047
+ /workspaces/{workspaceId}/insights/ai-usage-spend/key-insights:
913
1048
  get:
914
- tags: [Integrations]
915
- summary: List AI-tool spend
916
- 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
917
1056
  parameters:
918
1057
  - $ref: "#/components/parameters/WorkspaceId"
919
- - name: provider
920
- in: path
921
- required: true
1058
+ - name: rangeId
1059
+ in: query
1060
+ description: Measure time range (maps to weekly bucket count).
922
1061
  schema:
923
1062
  type: string
924
- enum: [cursor]
925
- - name: cycleStartMs
1063
+ enum: [4w, 8w, q]
1064
+ default: 4w
1065
+ - $ref: "#/components/parameters/InsightsDepartmentFilter"
1066
+ - name: team
926
1067
  in: query
927
- required: false
1068
+ description: Team name filter; send `All teams` to include every team.
928
1069
  schema:
929
- type: integer
930
- 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
931
1078
  responses:
932
1079
  "200":
933
- 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
934
1087
  content:
935
1088
  application/json:
936
1089
  schema:
937
1090
  type: object
938
- required: [rows]
1091
+ required: [error]
939
1092
  properties:
940
- rows:
941
- type: array
942
- items:
943
- $ref: "#/components/schemas/AiToolSpendRowDto"
1093
+ error: { type: string, example: invalid_query }
944
1094
  "401":
945
1095
  $ref: "#/components/responses/Unauthorized"
946
1096
  "403":
947
1097
  $ref: "#/components/responses/Forbidden"
1098
+ "404":
1099
+ description: Workspace does not exist or malformed workspaceId
948
1100
 
949
- /workspaces/{workspaceId}/integrations/ai-tool/{provider}/status:
1101
+ /workspaces/{workspaceId}/insights/ai-usage-spend/ai-assisted-code-percentage:
950
1102
  get:
951
- tags: [Integrations]
952
- summary: Get last refresh status
953
- 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
954
1113
  parameters:
955
1114
  - $ref: "#/components/parameters/WorkspaceId"
956
- - name: provider
957
- in: path
958
- required: true
1115
+ - name: rangeId
1116
+ in: query
1117
+ description: Measure time range (maps to weekly bucket count).
959
1118
  schema:
960
1119
  type: string
961
- 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
962
1135
  responses:
963
1136
  "200":
964
- 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
965
1144
  content:
966
1145
  application/json:
967
1146
  schema:
968
1147
  type: object
969
- required: [status]
1148
+ required: [error]
970
1149
  properties:
971
- status:
972
- $ref: "#/components/schemas/AiToolRefreshStatusDto"
1150
+ error: { type: string, example: invalid_query }
973
1151
  "401":
974
1152
  $ref: "#/components/responses/Unauthorized"
975
1153
  "403":
976
1154
  $ref: "#/components/responses/Forbidden"
977
1155
  "404":
978
- description: No refresh status yet (or provider source missing)
979
- content:
980
- application/json:
981
- schema:
982
- $ref: "#/components/schemas/AiToolRefreshResponseDto"
983
- example:
984
- ok: false
985
- error: no_source
1156
+ description: Workspace does not exist or malformed workspaceId
986
1157
 
987
- /integrations/github/callback:
1158
+ /workspaces/{workspaceId}/insights/ai-usage-spend/ai-tool-usage-spend:
988
1159
  get:
989
- tags: [Integrations]
990
- summary: GitHub App installation callback
991
- operationId: getGithubInstallCallback
1160
+ tags: [Workspace]
1161
+ summary: AI usage & spend insights — AI tool usage and spend by provider/model
992
1162
  description: >
993
- Public callback endpoint used by GitHub App installation flow.
994
- Resolves and consumes install state, persists workspace installation linkage,
995
- 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
996
1168
  parameters:
997
- - name: state
998
- in: query
999
- required: true
1000
- schema:
1001
- type: string
1002
- - name: installation_id
1003
- in: query
1004
- required: false
1005
- schema:
1006
- type: string
1007
- - name: setup_action
1169
+ - $ref: "#/components/parameters/WorkspaceId"
1170
+ - name: rangeId
1008
1171
  in: query
1009
- required: false
1172
+ description: Measure time range (maps to weekly bucket count).
1010
1173
  schema:
1011
1174
  type: string
1012
- - name: error
1175
+ enum: [4w, 8w, q]
1176
+ default: 4w
1177
+ - $ref: "#/components/parameters/InsightsDepartmentFilter"
1178
+ - name: team
1013
1179
  in: query
1014
- required: false
1180
+ description: Team name filter; send `All teams` to include every team.
1015
1181
  schema:
1016
1182
  type: string
1017
- - name: error_description
1183
+ default: All teams
1184
+ - name: windowEnd
1018
1185
  in: query
1019
- required: false
1186
+ description: ISO timestamp for the end of the last weekly bucket; omit for current week.
1020
1187
  schema:
1021
1188
  type: string
1189
+ format: date-time
1022
1190
  responses:
1023
- "302":
1024
- description: Redirect to ThinkAI SPA return path with GitHub connection status
1025
- "501":
1026
- description: Workspace store not configured
1191
+ "200":
1192
+ description: Provider and model AI tool usage/spend rollups
1027
1193
  content:
1028
1194
  application/json:
1029
1195
  schema:
1030
- $ref: "#/components/schemas/ErrorMessageDto"
1031
- "503":
1032
- description: SPA origin configuration missing
1196
+ $ref: "#/components/schemas/AiUsageSpendAiToolUsageSpendDto"
1197
+ "400":
1198
+ description: Invalid query parameters
1033
1199
  content:
1034
1200
  application/json:
1035
1201
  schema:
1036
- $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
1037
1212
 
1038
- /integrations/github/webhook:
1039
- post:
1040
- tags: [Integrations]
1041
- summary: GitHub App webhook receiver
1042
- 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
1043
1217
  description: >
1044
- Public webhook endpoint that verifies `X-Hub-Signature-256` and updates or
1045
- 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
1046
1223
  parameters:
1047
- - name: X-Hub-Signature-256
1048
- in: header
1049
- required: true
1224
+ - $ref: "#/components/parameters/WorkspaceId"
1225
+ - name: rangeId
1226
+ in: query
1227
+ description: Measure time range (maps to weekly bucket count).
1050
1228
  schema:
1051
1229
  type: string
1052
- - name: X-GitHub-Event
1053
- in: header
1054
- 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.
1055
1236
  schema:
1056
1237
  type: string
1057
- requestBody:
1058
- required: true
1059
- content:
1060
- application/json:
1061
- schema:
1062
- type: object
1063
- 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
1064
1245
  responses:
1065
1246
  "200":
1066
- description: Webhook accepted
1067
- "400":
1068
- description: Invalid payload
1247
+ description: Team weekly AI usage heatmap rows
1069
1248
  content:
1070
1249
  application/json:
1071
1250
  schema:
1072
- $ref: "#/components/schemas/ErrorMessageDto"
1073
- "401":
1074
- description: Invalid webhook signature
1075
- content:
1076
- application/json:
1077
- schema:
1078
- $ref: "#/components/schemas/ErrorMessageDto"
1079
- "501":
1080
- description: Workspace store not configured
1081
- content:
1082
- application/json:
1083
- schema:
1084
- $ref: "#/components/schemas/ErrorMessageDto"
1085
- "503":
1086
- description: Missing GitHub webhook secret or GitHub App configuration
1251
+ $ref: "#/components/schemas/AiUsageSpendTeamToolHeatmapDto"
1252
+ "400":
1253
+ description: Invalid query parameters
1087
1254
  content:
1088
1255
  application/json:
1089
1256
  schema:
1090
- $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
1091
1267
 
1092
- /workspaces/{workspaceId}/ai-readiness:
1268
+ /workspaces/{workspaceId}/insights/ai-usage-spend/roi-productivity-signal:
1093
1269
  get:
1094
- tags: [AIReadiness]
1095
- summary: Get AI readiness
1096
- 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
1097
1278
  parameters:
1098
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
1099
1300
  responses:
1100
1301
  "200":
1101
- description: State or JSON null
1302
+ description: ROI and productivity signal rollups
1102
1303
  content:
1103
1304
  application/json:
1104
1305
  schema:
1105
- $ref: "#/components/schemas/AIReadinessDto"
1106
- "401":
1107
- $ref: "#/components/responses/Unauthorized"
1108
- put:
1109
- tags: [AIReadiness]
1110
- summary: Upsert AI readiness
1111
- operationId: putAiReadiness
1112
- parameters:
1113
- - $ref: "#/components/parameters/WorkspaceId"
1114
- requestBody:
1115
- required: true
1116
- content:
1117
- application/json:
1118
- schema:
1119
- $ref: "#/components/schemas/AIReadinessDto"
1120
- responses:
1121
- "204":
1122
- description: Saved
1123
- "401":
1124
- $ref: "#/components/responses/Unauthorized"
1125
-
1126
- /workspaces/{workspaceId}/ai-readiness/maturity:
1127
- post:
1128
- tags: [AIReadiness]
1129
- summary: Compute maturity level
1130
- operationId: postAiReadinessMaturity
1131
- parameters:
1132
- - $ref: "#/components/parameters/WorkspaceId"
1133
- requestBody:
1134
- required: true
1135
- content:
1136
- application/json:
1137
- schema:
1138
- $ref: "#/components/schemas/AIReadinessDto"
1139
- responses:
1140
- "200":
1141
- description: Derived level
1306
+ $ref: "#/components/schemas/AiUsageSpendRoiProductivitySignalDto"
1307
+ "400":
1308
+ description: Invalid query parameters
1142
1309
  content:
1143
1310
  application/json:
1144
1311
  schema:
1145
- $ref: "#/components/schemas/AiMaturityResultDto"
1312
+ type: object
1313
+ required: [error]
1314
+ properties:
1315
+ error: { type: string, example: invalid_query }
1146
1316
  "401":
1147
1317
  $ref: "#/components/responses/Unauthorized"
1318
+ "403":
1319
+ $ref: "#/components/responses/Forbidden"
1320
+ "404":
1321
+ description: Workspace does not exist or malformed workspaceId
1148
1322
 
1149
- /workspaces/{workspaceId}/tech-stack:
1323
+ /workspaces/{workspaceId}/company-profile:
1150
1324
  get:
1151
- tags: [TechStack]
1152
- summary: Get tech stack
1153
- operationId: getTechStack
1325
+ tags: [CompanyProfile]
1326
+ summary: Get company profile
1327
+ operationId: getCompanyProfile
1154
1328
  parameters:
1155
1329
  - $ref: "#/components/parameters/WorkspaceId"
1156
1330
  responses:
1157
1331
  "200":
1158
1332
  description: >
1159
- Saved tech stack DTO, or JSON `null` when the workspace exists but no stack
1160
- 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.
1161
1337
  content:
1162
1338
  application/json:
1163
1339
  schema:
1164
- nullable: true
1165
- allOf:
1166
- - $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"
1167
1350
  "400":
1168
1351
  description: Malformed workspaceId
1169
1352
  content:
@@ -1181,9 +1364,14 @@ paths:
1181
1364
  schema:
1182
1365
  $ref: "#/components/schemas/ErrorMessageDto"
1183
1366
  put:
1184
- tags: [TechStack]
1185
- summary: Upsert tech stack
1186
- 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.
1187
1375
  parameters:
1188
1376
  - $ref: "#/components/parameters/WorkspaceId"
1189
1377
  requestBody:
@@ -1191,12 +1379,12 @@ paths:
1191
1379
  content:
1192
1380
  application/json:
1193
1381
  schema:
1194
- $ref: "#/components/schemas/TechStackDto"
1382
+ $ref: "#/components/schemas/CompanyProfileDto"
1195
1383
  responses:
1196
1384
  "204":
1197
1385
  description: Saved
1198
1386
  "400":
1199
- description: Body fails validation (unknown keys, bounds, missing required fields)
1387
+ description: Body fails validation (missing/invalid required field, bad enum, conditional rule)
1200
1388
  content:
1201
1389
  application/json:
1202
1390
  schema:
@@ -1212,406 +1400,522 @@ paths:
1212
1400
  schema:
1213
1401
  $ref: "#/components/schemas/ErrorMessageDto"
1214
1402
 
1215
- /workspaces/{workspaceId}/nda:
1403
+ /workspaces/{workspaceId}/integrations/github:
1216
1404
  get:
1217
- tags: [NDA]
1218
- summary: Get NDA completion state
1219
- 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.
1220
1412
  parameters:
1221
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
1222
1430
  responses:
1223
1431
  "200":
1224
- description: Completion or JSON null
1432
+ description: Installation status
1225
1433
  content:
1226
1434
  application/json:
1227
1435
  schema:
1228
- $ref: "#/components/schemas/NdaCompletionDto"
1436
+ $ref: "#/components/schemas/GithubInstallationStatusDto"
1229
1437
  "401":
1230
1438
  $ref: "#/components/responses/Unauthorized"
1231
-
1232
- /workspaces/{workspaceId}/nda/template:
1233
- get:
1234
- tags: [NDA]
1235
- summary: NDA template for display
1236
- operationId: getNdaTemplate
1237
- parameters:
1238
- - $ref: "#/components/parameters/WorkspaceId"
1239
- responses:
1240
- "200":
1241
- description: Template
1439
+ "403":
1440
+ $ref: "#/components/responses/Forbidden"
1441
+ "404":
1442
+ description: Workspace does not exist or malformed workspaceId
1242
1443
  content:
1243
1444
  application/json:
1244
1445
  schema:
1245
- $ref: "#/components/schemas/NdaTemplateDto"
1246
- "401":
1247
- $ref: "#/components/responses/Unauthorized"
1446
+ $ref: "#/components/schemas/ErrorMessageDto"
1248
1447
 
1249
- /workspaces/{workspaceId}/nda/sign:
1250
- post:
1251
- tags: [NDA]
1252
- summary: Electronic NDA signature
1253
- operationId: postNdaSign
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: []
1254
1458
  parameters:
1255
1459
  - $ref: "#/components/parameters/WorkspaceId"
1256
- requestBody:
1257
- required: true
1258
- content:
1259
- application/json:
1260
- schema:
1261
- $ref: "#/components/schemas/NdaSignatureDto"
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
1262
1472
  responses:
1263
- "200":
1264
- description: Completion record
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
1265
1481
  content:
1266
1482
  application/json:
1267
1483
  schema:
1268
- $ref: "#/components/schemas/NdaCompletionDto"
1269
- "401":
1270
- $ref: "#/components/responses/Unauthorized"
1484
+ $ref: "#/components/schemas/ErrorMessageDto"
1271
1485
 
1272
- /workspaces/{workspaceId}/nda/external:
1486
+ /workspaces/{workspaceId}/integrations/github/install-url:
1273
1487
  post:
1274
- tags: [NDA]
1275
- summary: External NDA path (after Supabase upload)
1488
+ tags: [Integrations]
1489
+ summary: Create GitHub App installation URL
1490
+ operationId: postGithubInstallUrl
1276
1491
  description: >
1277
- Preferred body uses `storagePath` after client upload to the `nda-documents` bucket (§14).
1278
- Multipart variant may be supported by the server as an alternative.
1279
- operationId: postNdaExternal
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: []
1280
1496
  parameters:
1281
1497
  - $ref: "#/components/parameters/WorkspaceId"
1282
1498
  requestBody:
1283
- required: true
1499
+ required: false
1284
1500
  content:
1285
1501
  application/json:
1286
1502
  schema:
1287
- $ref: "#/components/schemas/NdaExternalBodyDto"
1503
+ $ref: "#/components/schemas/GithubInstallUrlRequestDto"
1288
1504
  responses:
1289
1505
  "200":
1290
- description: Completion record
1506
+ description: Installation URL + signed state
1291
1507
  content:
1292
1508
  application/json:
1293
1509
  schema:
1294
- $ref: "#/components/schemas/NdaCompletionDto"
1295
- "401":
1296
- $ref: "#/components/responses/Unauthorized"
1297
-
1298
- /workspaces/{workspaceId}/readiness/repos:
1299
- get:
1300
- tags: [RepositoryReadiness]
1301
- summary: List repo readiness scores
1302
- operationId: listReadinessRepos
1303
- parameters:
1304
- - $ref: "#/components/parameters/WorkspaceId"
1305
- responses:
1306
- "200":
1307
- description: Repositories analyzed
1510
+ $ref: "#/components/schemas/GithubInstallUrlResponseDto"
1511
+ "400":
1512
+ description: Request validation failed
1308
1513
  content:
1309
1514
  application/json:
1310
1515
  schema:
1311
- type: array
1312
- items:
1313
- $ref: "#/components/schemas/RepoReadinessScoreDto"
1516
+ $ref: "#/components/schemas/ErrorMessageDto"
1314
1517
  "401":
1315
1518
  $ref: "#/components/responses/Unauthorized"
1316
1519
  "403":
1317
1520
  $ref: "#/components/responses/Forbidden"
1318
1521
  "404":
1319
- description: Workspace does not exist
1522
+ description: Workspace does not exist or malformed workspaceId
1320
1523
  content:
1321
1524
  application/json:
1322
1525
  schema:
1323
1526
  $ref: "#/components/schemas/ErrorMessageDto"
1324
1527
 
1325
- /workspaces/{workspaceId}/readiness/analyze:
1528
+ /workspaces/{workspaceId}/integrations/ai-tool/{provider}/refresh:
1326
1529
  post:
1327
- tags: [RepositoryReadiness]
1328
- summary: Enqueue AI readiness analysis for one repository
1530
+ tags: [Integrations]
1531
+ summary: Refresh AI tool integration snapshots
1532
+ operationId: refreshWorkspaceAiTool
1329
1533
  description: >
1330
- User-triggered "Run now" path. Requires workspace editor/admin access and an active GitHub App
1331
- connection. Prefer `repoId`; `providerSlug` is accepted for compatibility only when it resolves
1332
- to a repository in the workspace.
1333
- operationId: postReadinessAnalyze
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.
1334
1536
  parameters:
1335
1537
  - $ref: "#/components/parameters/WorkspaceId"
1336
- requestBody:
1337
- required: true
1338
- content:
1339
- application/json:
1340
- schema:
1341
- $ref: "#/components/schemas/ReadinessAnalyzeBodyDto"
1342
- examples:
1343
- byRepoId:
1344
- value:
1345
- repoId: "11111111-1111-4111-8111-111111111111"
1346
- byProviderSlug:
1347
- value:
1348
- providerSlug: "thinkai-llc/thinkai-platform-backend"
1538
+ - name: provider
1539
+ in: path
1540
+ required: true
1541
+ schema:
1542
+ type: string
1543
+ enum: [cursor]
1349
1544
  responses:
1350
- "202":
1351
- description: Run queued
1352
- content:
1353
- application/json:
1354
- schema:
1355
- $ref: "#/components/schemas/ReadinessAnalyzeResponseDto"
1356
- "400":
1357
- description: Invalid body, missing GitHub connection, unsupported provider, or active run conflict inputs
1545
+ "200":
1546
+ description: Refresh result
1358
1547
  content:
1359
1548
  application/json:
1360
1549
  schema:
1361
- $ref: "#/components/schemas/ErrorMessageDto"
1550
+ $ref: "#/components/schemas/AiToolRefreshResponseDto"
1551
+ examples:
1552
+ ok:
1553
+ value: { ok: true }
1554
+ invalid_token:
1555
+ value: { ok: false, error: invalid_token }
1362
1556
  "401":
1363
1557
  $ref: "#/components/responses/Unauthorized"
1364
1558
  "403":
1365
1559
  $ref: "#/components/responses/Forbidden"
1366
1560
  "404":
1367
- description: Workspace or repository not found
1561
+ description: Provider source missing for this workspace
1368
1562
  content:
1369
1563
  application/json:
1370
1564
  schema:
1371
- $ref: "#/components/schemas/ErrorMessageDto"
1565
+ $ref: "#/components/schemas/AiToolRefreshResponseDto"
1566
+ example:
1567
+ ok: false
1568
+ error: no_source
1372
1569
  "409":
1373
- description: A queued/running readiness analysis already exists for this repository slug
1570
+ description: Refresh already in progress
1374
1571
  content:
1375
1572
  application/json:
1376
1573
  schema:
1377
- $ref: "#/components/schemas/ErrorMessageDto"
1574
+ $ref: "#/components/schemas/AiToolRefreshResponseDto"
1575
+ example:
1576
+ ok: false
1577
+ error: already_in_progress
1378
1578
 
1379
- /workspaces/{workspaceId}/readiness/runs/{runId}:
1579
+ /workspaces/{workspaceId}/integrations/ai-tool/{provider}/seats:
1380
1580
  get:
1381
- tags: [RepositoryReadiness]
1382
- summary: Get readiness analysis run status
1383
- operationId: getReadinessRun
1581
+ tags: [Integrations]
1582
+ summary: List latest AI-tool seats
1583
+ operationId: getWorkspaceAiToolSeats
1384
1584
  parameters:
1385
1585
  - $ref: "#/components/parameters/WorkspaceId"
1386
- - name: runId
1586
+ - name: provider
1387
1587
  in: path
1388
1588
  required: true
1389
1589
  schema:
1390
1590
  type: string
1391
- format: uuid
1591
+ enum: [cursor]
1392
1592
  responses:
1393
1593
  "200":
1394
- description: Run status
1594
+ description: Seats
1395
1595
  content:
1396
1596
  application/json:
1397
1597
  schema:
1398
- $ref: "#/components/schemas/ReadinessRunSummaryDto"
1598
+ type: object
1599
+ required: [seats]
1600
+ properties:
1601
+ seats:
1602
+ type: array
1603
+ items:
1604
+ $ref: "#/components/schemas/AiToolSeatDto"
1399
1605
  "401":
1400
1606
  $ref: "#/components/responses/Unauthorized"
1401
1607
  "403":
1402
1608
  $ref: "#/components/responses/Forbidden"
1403
- "404":
1404
- description: Workspace or run not found
1405
- content:
1406
- application/json:
1407
- schema:
1408
- $ref: "#/components/schemas/ErrorMessageDto"
1409
1609
 
1410
- /workspaces/{workspaceId}/readiness/repos/{repoId}:
1610
+ /workspaces/{workspaceId}/integrations/ai-tool/{provider}/daily-usage:
1411
1611
  get:
1412
- tags: [RepositoryReadiness]
1413
- summary: Single repo readiness
1414
- description: >
1415
- Returns one stored repository scorecard with all eight dimensions and nested issues.
1416
- Unknown `repoId` or a repo that belongs to another workspace returns `404` (no cross-tenant leakage).
1417
- operationId: getReadinessRepo
1612
+ tags: [Integrations]
1613
+ summary: List AI-tool daily usage
1614
+ operationId: getWorkspaceAiToolDailyUsage
1418
1615
  parameters:
1419
1616
  - $ref: "#/components/parameters/WorkspaceId"
1420
- - $ref: "#/components/parameters/RepoId"
1617
+ - name: provider
1618
+ in: path
1619
+ required: true
1620
+ schema:
1621
+ type: string
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
1421
1640
  responses:
1422
1641
  "200":
1423
- description: Repo score
1424
- content:
1425
- application/json:
1426
- schema:
1427
- $ref: "#/components/schemas/RepoReadinessScoreDto"
1428
- "400":
1429
- description: Malformed `repoId` (not a UUID)
1642
+ description: Usage rows
1430
1643
  content:
1431
1644
  application/json:
1432
1645
  schema:
1433
- $ref: "#/components/schemas/ErrorMessageDto"
1646
+ type: object
1647
+ required: [rows]
1648
+ properties:
1649
+ rows:
1650
+ type: array
1651
+ items:
1652
+ $ref: "#/components/schemas/AiToolDailyUsageRowDto"
1434
1653
  "401":
1435
1654
  $ref: "#/components/responses/Unauthorized"
1436
1655
  "403":
1437
1656
  $ref: "#/components/responses/Forbidden"
1438
- "404":
1439
- description: Workspace or repository not found
1657
+
1658
+ /workspaces/{workspaceId}/integrations/ai-tool/{provider}/spend:
1659
+ get:
1660
+ tags: [Integrations]
1661
+ summary: List AI-tool spend
1662
+ operationId: getWorkspaceAiToolSpend
1663
+ parameters:
1664
+ - $ref: "#/components/parameters/WorkspaceId"
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
1677
+ responses:
1678
+ "200":
1679
+ description: Spend rows
1440
1680
  content:
1441
1681
  application/json:
1442
1682
  schema:
1443
- $ref: "#/components/schemas/ErrorMessageDto"
1683
+ type: object
1684
+ required: [rows]
1685
+ properties:
1686
+ rows:
1687
+ type: array
1688
+ items:
1689
+ $ref: "#/components/schemas/AiToolSpendRowDto"
1690
+ "401":
1691
+ $ref: "#/components/responses/Unauthorized"
1692
+ "403":
1693
+ $ref: "#/components/responses/Forbidden"
1444
1694
 
1445
- /workspaces/{workspaceId}/agentic-foundation/dashboard:
1695
+ /workspaces/{workspaceId}/integrations/ai-tool/{provider}/status:
1446
1696
  get:
1447
- tags: [AgenticFoundation]
1448
- summary: Agentic Foundation dashboard
1449
- description: >
1450
- Returns stored repository readiness scorecards plus a flattened issue queue for the
1451
- Agentic Foundation dashboard surfaces. This is read-only and does not run scoring on
1452
- the request path (issue #279). Field `generatedAt` is the server timestamp when this JSON
1453
- payload was assembled; it is not the same as per-repo `lastAnalyzed` on `RepoReadinessScoreDto`.
1454
- operationId: getAgenticFoundationDashboard
1697
+ tags: [Integrations]
1698
+ summary: Get last refresh status
1699
+ operationId: getWorkspaceAiToolRefreshStatus
1455
1700
  parameters:
1456
1701
  - $ref: "#/components/parameters/WorkspaceId"
1702
+ - name: provider
1703
+ in: path
1704
+ required: true
1705
+ schema:
1706
+ type: string
1707
+ enum: [cursor]
1457
1708
  responses:
1458
1709
  "200":
1459
- description: Dashboard scorecards and issue queue
1710
+ description: Refresh status
1460
1711
  content:
1461
1712
  application/json:
1462
1713
  schema:
1463
- $ref: "#/components/schemas/AgenticFoundationDashboardDto"
1464
- examples:
1465
- emptyWorkspace:
1466
- summary: No repos analyzed yet
1467
- value:
1468
- workspaceId: 00000000-0000-0000-0000-000000000001
1469
- generatedAt: "2026-05-12T12:00:00.000Z"
1470
- summary:
1471
- repoCount: 0
1472
- averageOverallScore: 0
1473
- issueCount: 0
1474
- criticalIssueCount: 0
1475
- repos: []
1476
- issueQueue: []
1714
+ type: object
1715
+ required: [status]
1716
+ properties:
1717
+ status:
1718
+ $ref: "#/components/schemas/AiToolRefreshStatusDto"
1477
1719
  "401":
1478
1720
  $ref: "#/components/responses/Unauthorized"
1479
1721
  "403":
1480
1722
  $ref: "#/components/responses/Forbidden"
1481
1723
  "404":
1482
- description: Workspace does not exist
1724
+ description: No refresh status yet (or provider source missing)
1483
1725
  content:
1484
1726
  application/json:
1485
1727
  schema:
1486
- $ref: "#/components/schemas/ErrorMessageDto"
1728
+ $ref: "#/components/schemas/AiToolRefreshResponseDto"
1729
+ example:
1730
+ ok: false
1731
+ error: no_source
1487
1732
 
1488
- /workspaces/{workspaceId}/agentic-foundation/agentic-readiness:
1733
+ /integrations/github/callback:
1489
1734
  get:
1490
- tags: [AgenticFoundation]
1491
- summary: Agentic readiness pillar (stored dimension slice)
1735
+ tags: [Integrations]
1736
+ summary: GitHub App installation callback
1737
+ operationId: getGithubInstallCallback
1492
1738
  description: >
1493
- Returns `RepoReadinessScoreDto` rows with only the `ai-agent-readiness` dimension populated
1494
- per repo. Same auth and persistence source as `GET .../readiness/repos` (issue #280).
1495
- operationId: getAgenticFoundationAgenticReadiness
1496
- parameters:
1497
- - $ref: "#/components/parameters/WorkspaceId"
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.
1742
+ parameters:
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
1498
1768
  responses:
1499
- "200":
1500
- 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
1501
1773
  content:
1502
1774
  application/json:
1503
1775
  schema:
1504
- type: array
1505
- items:
1506
- $ref: "#/components/schemas/RepoReadinessScoreDto"
1507
- examples:
1508
- singleRepoSlice:
1509
- summary: One repo, single pillar dimension
1510
- value:
1511
- - repoId: 00000000-0000-0000-0000-000000000002
1512
- repoName: api-gateway
1513
- language: typescript
1514
- overallScore: 67
1515
- dimensions:
1516
- - dimensionId: ai-agent-readiness
1517
- score: 2
1518
- maxScore: 3
1519
- issues: []
1520
- lastAnalyzed: "2026-05-12T12:00:00.000Z"
1521
- teamId: null
1522
- provider:
1523
- kind: github
1524
- slug: acme/api-gateway
1525
- webUrl: https://github.com/acme/api-gateway
1526
- defaultBranch: main
1527
- externalId: null
1528
- hostBaseUrl: null
1529
- analysisStatus: idle
1530
- activeRunId: null
1531
- "401":
1532
- $ref: "#/components/responses/Unauthorized"
1533
- "403":
1534
- $ref: "#/components/responses/Forbidden"
1535
- "404":
1536
- description: Workspace does not exist
1776
+ $ref: "#/components/schemas/ErrorMessageDto"
1777
+ "503":
1778
+ description: SPA origin configuration missing
1537
1779
  content:
1538
1780
  application/json:
1539
1781
  schema:
1540
1782
  $ref: "#/components/schemas/ErrorMessageDto"
1541
1783
 
1542
- /workspaces/{workspaceId}/agentic-foundation/repository-health:
1543
- get:
1544
- tags: [AgenticFoundation]
1545
- summary: Repository health pillar (repo-hygiene slice)
1546
- description: Returns per-repo `repo-hygiene` dimension only (issue #281).
1547
- 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.
1548
1792
  parameters:
1549
- - $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
1550
1810
  responses:
1551
1811
  "200":
1552
- description: Repositories with repo-hygiene slice
1812
+ description: Webhook accepted
1813
+ "400":
1814
+ description: Invalid payload
1553
1815
  content:
1554
1816
  application/json:
1555
1817
  schema:
1556
- type: array
1557
- items:
1558
- $ref: "#/components/schemas/RepoReadinessScoreDto"
1818
+ $ref: "#/components/schemas/ErrorMessageDto"
1559
1819
  "401":
1560
- $ref: "#/components/responses/Unauthorized"
1561
- "403":
1562
- $ref: "#/components/responses/Forbidden"
1563
- "404":
1564
- 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
1565
1833
  content:
1566
1834
  application/json:
1567
1835
  schema:
1568
1836
  $ref: "#/components/schemas/ErrorMessageDto"
1569
1837
 
1570
- /workspaces/{workspaceId}/agentic-foundation/codebase-quality:
1838
+ /workspaces/{workspaceId}/ai-readiness:
1571
1839
  get:
1572
- tags: [AgenticFoundation]
1573
- summary: Codebase quality pillar (stack-quality slice)
1574
- description: Returns per-repo `stack-quality` dimension only (issue #282).
1575
- operationId: getAgenticFoundationCodebaseQuality
1840
+ tags: [AIReadiness]
1841
+ summary: Get AI readiness
1842
+ operationId: getAiReadiness
1576
1843
  parameters:
1577
1844
  - $ref: "#/components/parameters/WorkspaceId"
1578
1845
  responses:
1579
1846
  "200":
1580
- description: Repositories with stack-quality slice
1847
+ description: State or JSON null
1581
1848
  content:
1582
1849
  application/json:
1583
1850
  schema:
1584
- type: array
1585
- items:
1586
- $ref: "#/components/schemas/RepoReadinessScoreDto"
1851
+ $ref: "#/components/schemas/AIReadinessDto"
1587
1852
  "401":
1588
1853
  $ref: "#/components/responses/Unauthorized"
1589
- "403":
1590
- $ref: "#/components/responses/Forbidden"
1591
- "404":
1592
- 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
1593
1888
  content:
1594
1889
  application/json:
1595
1890
  schema:
1596
- $ref: "#/components/schemas/ErrorMessageDto"
1891
+ $ref: "#/components/schemas/AiMaturityResultDto"
1892
+ "401":
1893
+ $ref: "#/components/responses/Unauthorized"
1597
1894
 
1598
- /workspaces/{workspaceId}/agentic-foundation/test-qa:
1895
+ /workspaces/{workspaceId}/tech-stack:
1599
1896
  get:
1600
- tags: [AgenticFoundation]
1601
- summary: Test and QA pillar (tests-qa slice)
1602
- description: Returns per-repo `tests-qa` dimension only (issue #283).
1603
- operationId: getAgenticFoundationTestQa
1897
+ tags: [TechStack]
1898
+ summary: Get tech stack
1899
+ operationId: getTechStack
1604
1900
  parameters:
1605
1901
  - $ref: "#/components/parameters/WorkspaceId"
1606
1902
  responses:
1607
1903
  "200":
1608
- 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.
1609
1907
  content:
1610
1908
  application/json:
1611
1909
  schema:
1612
- type: array
1613
- items:
1614
- $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"
1615
1919
  "401":
1616
1920
  $ref: "#/components/responses/Unauthorized"
1617
1921
  "403":
@@ -1622,24 +1926,27 @@ paths:
1622
1926
  application/json:
1623
1927
  schema:
1624
1928
  $ref: "#/components/schemas/ErrorMessageDto"
1625
-
1626
- /workspaces/{workspaceId}/agentic-foundation/observability:
1627
- get:
1628
- tags: [AgenticFoundation]
1629
- summary: Observability pillar (observability slice)
1630
- description: Returns per-repo `observability` dimension only (issue #284).
1631
- operationId: getAgenticFoundationObservability
1929
+ put:
1930
+ tags: [TechStack]
1931
+ summary: Upsert tech stack
1932
+ operationId: putTechStack
1632
1933
  parameters:
1633
1934
  - $ref: "#/components/parameters/WorkspaceId"
1935
+ requestBody:
1936
+ required: true
1937
+ content:
1938
+ application/json:
1939
+ schema:
1940
+ $ref: "#/components/schemas/TechStackDto"
1634
1941
  responses:
1635
- "200":
1636
- description: Repositories with observability slice
1942
+ "204":
1943
+ description: Saved
1944
+ "400":
1945
+ description: Body fails validation (unknown keys, bounds, missing required fields)
1637
1946
  content:
1638
1947
  application/json:
1639
1948
  schema:
1640
- type: array
1641
- items:
1642
- $ref: "#/components/schemas/RepoReadinessScoreDto"
1949
+ $ref: "#/components/schemas/ErrorMessageDto"
1643
1950
  "401":
1644
1951
  $ref: "#/components/responses/Unauthorized"
1645
1952
  "403":
@@ -1651,103 +1958,99 @@ paths:
1651
1958
  schema:
1652
1959
  $ref: "#/components/schemas/ErrorMessageDto"
1653
1960
 
1654
- /workspaces/{workspaceId}/agentic-foundation/api-contract:
1961
+ /workspaces/{workspaceId}/nda:
1655
1962
  get:
1656
- tags: [AgenticFoundation]
1657
- summary: API contract pillar (api-contracts slice)
1658
- description: Returns per-repo `api-contracts` dimension only (issue #285).
1659
- operationId: getAgenticFoundationApiContract
1963
+ tags: [NDA]
1964
+ summary: Get NDA completion state
1965
+ operationId: getNda
1660
1966
  parameters:
1661
1967
  - $ref: "#/components/parameters/WorkspaceId"
1662
1968
  responses:
1663
1969
  "200":
1664
- description: Repositories with api-contracts slice
1970
+ description: Completion or JSON null
1665
1971
  content:
1666
1972
  application/json:
1667
1973
  schema:
1668
- type: array
1669
- items:
1670
- $ref: "#/components/schemas/RepoReadinessScoreDto"
1974
+ $ref: "#/components/schemas/NdaCompletionDto"
1671
1975
  "401":
1672
1976
  $ref: "#/components/responses/Unauthorized"
1673
- "403":
1674
- $ref: "#/components/responses/Forbidden"
1675
- "404":
1676
- description: Workspace does not exist
1677
- content:
1678
- application/json:
1679
- schema:
1680
- $ref: "#/components/schemas/ErrorMessageDto"
1681
1977
 
1682
- /workspaces/{workspaceId}/agentic-foundation/platform-governance:
1978
+ /workspaces/{workspaceId}/nda/template:
1683
1979
  get:
1684
- tags: [AgenticFoundation]
1685
- summary: Platform governance pillar (platform-governance slice)
1686
- description: Returns per-repo `platform-governance` dimension only (issue #286).
1687
- operationId: getAgenticFoundationPlatformGovernance
1980
+ tags: [NDA]
1981
+ summary: NDA template for display
1982
+ operationId: getNdaTemplate
1688
1983
  parameters:
1689
1984
  - $ref: "#/components/parameters/WorkspaceId"
1690
1985
  responses:
1691
1986
  "200":
1692
- description: Repositories with platform-governance slice
1987
+ description: Template
1693
1988
  content:
1694
1989
  application/json:
1695
1990
  schema:
1696
- type: array
1697
- items:
1698
- $ref: "#/components/schemas/RepoReadinessScoreDto"
1991
+ $ref: "#/components/schemas/NdaTemplateDto"
1699
1992
  "401":
1700
1993
  $ref: "#/components/responses/Unauthorized"
1701
- "403":
1702
- $ref: "#/components/responses/Forbidden"
1703
- "404":
1704
- description: Workspace does not exist
1705
- content:
1706
- application/json:
1707
- schema:
1708
- $ref: "#/components/schemas/ErrorMessageDto"
1709
1994
 
1710
- /workspaces/{workspaceId}/agentic-foundation/delivery-governance:
1711
- get:
1712
- tags: [AgenticFoundation]
1713
- summary: Delivery governance pillar (delivery slice)
1714
- description: Returns per-repo `delivery` dimension only (issue #287).
1715
- operationId: getAgenticFoundationDeliveryGovernance
1995
+ /workspaces/{workspaceId}/nda/sign:
1996
+ post:
1997
+ tags: [NDA]
1998
+ summary: Electronic NDA signature
1999
+ operationId: postNdaSign
1716
2000
  parameters:
1717
2001
  - $ref: "#/components/parameters/WorkspaceId"
2002
+ requestBody:
2003
+ required: true
2004
+ content:
2005
+ application/json:
2006
+ schema:
2007
+ $ref: "#/components/schemas/NdaSignatureDto"
1718
2008
  responses:
1719
2009
  "200":
1720
- description: Repositories with delivery slice
2010
+ description: Completion record
1721
2011
  content:
1722
2012
  application/json:
1723
2013
  schema:
1724
- type: array
1725
- items:
1726
- $ref: "#/components/schemas/RepoReadinessScoreDto"
2014
+ $ref: "#/components/schemas/NdaCompletionDto"
1727
2015
  "401":
1728
2016
  $ref: "#/components/responses/Unauthorized"
1729
- "403":
1730
- $ref: "#/components/responses/Forbidden"
1731
- "404":
1732
- 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
1733
2037
  content:
1734
2038
  application/json:
1735
2039
  schema:
1736
- $ref: "#/components/schemas/ErrorMessageDto"
2040
+ $ref: "#/components/schemas/NdaCompletionDto"
2041
+ "401":
2042
+ $ref: "#/components/responses/Unauthorized"
1737
2043
 
1738
- /workspaces/{workspaceId}/agentic-foundation/ai-resolution-hub:
2044
+ /workspaces/{workspaceId}/readiness/repos:
1739
2045
  get:
1740
- tags: [AgenticFoundation]
1741
- summary: AI resolution hub (full readiness scorecards)
1742
- description: >
1743
- Returns the same payload as `GET .../readiness/repos` (all dimensions per repo) for
1744
- cross-pillar hub views (issue #288).
1745
- operationId: getAgenticFoundationAiResolutionHub
2046
+ tags: [RepositoryReadiness]
2047
+ summary: List repo readiness scores
2048
+ operationId: listReadinessRepos
1746
2049
  parameters:
1747
2050
  - $ref: "#/components/parameters/WorkspaceId"
1748
2051
  responses:
1749
2052
  "200":
1750
- description: Full repository readiness scorecards
2053
+ description: Repositories analyzed
1751
2054
  content:
1752
2055
  application/json:
1753
2056
  schema:
@@ -1765,94 +2068,111 @@ paths:
1765
2068
  schema:
1766
2069
  $ref: "#/components/schemas/ErrorMessageDto"
1767
2070
 
1768
- /workspaces/{workspaceId}/readiness/dimensions:
1769
- get:
2071
+ /workspaces/{workspaceId}/readiness/analyze:
2072
+ post:
1770
2073
  tags: [RepositoryReadiness]
1771
- summary: Readiness dimensions catalog
2074
+ summary: Enqueue AI readiness analysis for one repository
1772
2075
  description: >
1773
- Returns static rubric metadata (id, name, description, maxScore) aligned with the readiness
1774
- scanner dimensions. Does not run scoring on the request path.
1775
- 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
1776
2080
  parameters:
1777
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"
1778
2095
  responses:
1779
- "200":
1780
- description: Dimensions
2096
+ "202":
2097
+ description: Run queued
1781
2098
  content:
1782
2099
  application/json:
1783
2100
  schema:
1784
- type: array
1785
- items:
1786
- $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"
1787
2108
  "401":
1788
2109
  $ref: "#/components/responses/Unauthorized"
1789
2110
  "403":
1790
2111
  $ref: "#/components/responses/Forbidden"
1791
2112
  "404":
1792
- 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
1793
2120
  content:
1794
2121
  application/json:
1795
2122
  schema:
1796
2123
  $ref: "#/components/schemas/ErrorMessageDto"
1797
2124
 
1798
- /workspaces/{workspaceId}/readiness/fixes:
2125
+ /workspaces/{workspaceId}/readiness/runs/{runId}:
1799
2126
  get:
1800
2127
  tags: [RepositoryReadiness]
1801
- summary: List fix requests
1802
- description: >
1803
- Returns persisted fix-queue rows for the workspace (newest first). Empty array when none exist.
1804
- operationId: listReadinessFixes
2128
+ summary: Get readiness analysis run status
2129
+ operationId: getReadinessRun
1805
2130
  parameters:
1806
2131
  - $ref: "#/components/parameters/WorkspaceId"
2132
+ - name: runId
2133
+ in: path
2134
+ required: true
2135
+ schema:
2136
+ type: string
2137
+ format: uuid
1807
2138
  responses:
1808
2139
  "200":
1809
- description: Fix queue
2140
+ description: Run status
1810
2141
  content:
1811
2142
  application/json:
1812
2143
  schema:
1813
- type: array
1814
- items:
1815
- $ref: "#/components/schemas/FixRequestDto"
2144
+ $ref: "#/components/schemas/ReadinessRunSummaryDto"
1816
2145
  "401":
1817
2146
  $ref: "#/components/responses/Unauthorized"
1818
2147
  "403":
1819
2148
  $ref: "#/components/responses/Forbidden"
1820
2149
  "404":
1821
- description: Workspace does not exist
2150
+ description: Workspace or run not found
1822
2151
  content:
1823
2152
  application/json:
1824
2153
  schema:
1825
2154
  $ref: "#/components/schemas/ErrorMessageDto"
1826
- post:
2155
+
2156
+ /workspaces/{workspaceId}/readiness/repos/{repoId}:
2157
+ get:
1827
2158
  tags: [RepositoryReadiness]
1828
- summary: Enqueue fix for an issue
2159
+ summary: Single repo readiness
1829
2160
  description: >
1830
- Creates a fix request with `status: queued` when `issueId` belongs to `repoId` in this workspace.
1831
- Requires workspace editor or admin. Re-posting the same `issueId` and `repoId` returns the
1832
- existing row (idempotent).
1833
- 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
1834
2164
  parameters:
1835
2165
  - $ref: "#/components/parameters/WorkspaceId"
1836
- requestBody:
1837
- required: true
1838
- content:
1839
- application/json:
1840
- schema:
1841
- $ref: "#/components/schemas/CreateFixRequestBodyDto"
1842
- examples:
1843
- enqueue:
1844
- value:
1845
- issueId: "22222222-2222-2222-2222-222222222222"
1846
- repoId: "11111111-1111-4111-8111-111111111111"
2166
+ - $ref: "#/components/parameters/RepoId"
1847
2167
  responses:
1848
2168
  "200":
1849
- description: Fix request created or existing row returned
2169
+ description: Repo score
1850
2170
  content:
1851
2171
  application/json:
1852
2172
  schema:
1853
- $ref: "#/components/schemas/FixRequestDto"
2173
+ $ref: "#/components/schemas/RepoReadinessScoreDto"
1854
2174
  "400":
1855
- description: Invalid body or malformed `repoId` (not a UUID)
2175
+ description: Malformed `repoId` (not a UUID)
1856
2176
  content:
1857
2177
  application/json:
1858
2178
  schema:
@@ -1862,239 +2182,210 @@ paths:
1862
2182
  "403":
1863
2183
  $ref: "#/components/responses/Forbidden"
1864
2184
  "404":
1865
- description: Workspace not found, or issue/repo pair not in workspace
2185
+ description: Workspace or repository not found
1866
2186
  content:
1867
2187
  application/json:
1868
2188
  schema:
1869
2189
  $ref: "#/components/schemas/ErrorMessageDto"
1870
2190
 
1871
- /workspaces/{workspaceId}/readiness/fixes/{fixId}/retry:
1872
- post:
1873
- tags: [RepositoryReadiness]
1874
- summary: Retry a failed fix request
2191
+ /workspaces/{workspaceId}/agentic-foundation/dashboard:
2192
+ get:
2193
+ tags: [AgenticFoundation]
2194
+ summary: Agentic Foundation dashboard
1875
2195
  description: >
1876
- Re-queues a fix with `status: failed` when `errorCode` is one of
1877
- `FixRetryableErrorCode` (`PROVIDER_TIMEOUT`, `RATE_LIMITED`, `TRANSIENT_NETWORK`,
1878
- `RUNNER_INTERRUPTED`). Idempotent for the same failed row. Requires workspace editor or admin.
1879
- 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
1880
2201
  parameters:
1881
2202
  - $ref: "#/components/parameters/WorkspaceId"
1882
- - $ref: "#/components/parameters/FixId"
1883
2203
  responses:
1884
2204
  "200":
1885
- description: Fix re-queued
2205
+ description: Dashboard scorecards and issue queue
1886
2206
  content:
1887
2207
  application/json:
1888
2208
  schema:
1889
- $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: []
1890
2223
  "401":
1891
2224
  $ref: "#/components/responses/Unauthorized"
1892
2225
  "403":
1893
2226
  $ref: "#/components/responses/Forbidden"
1894
2227
  "404":
1895
- description: Fix not found in workspace
1896
- content:
1897
- application/json:
1898
- schema:
1899
- $ref: "#/components/schemas/ErrorMessageDto"
1900
- "409":
1901
- description: Fix is not in a retryable state
2228
+ description: Workspace does not exist
1902
2229
  content:
1903
2230
  application/json:
1904
2231
  schema:
1905
2232
  $ref: "#/components/schemas/ErrorMessageDto"
1906
2233
 
1907
- /workspaces/{workspaceId}/squad:
2234
+ /workspaces/{workspaceId}/agentic-foundation/agentic-readiness:
1908
2235
  get:
1909
- tags: [SquadProjects]
1910
- summary: Get squad configuration
1911
- operationId: getSquad
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
1912
2242
  parameters:
1913
2243
  - $ref: "#/components/parameters/WorkspaceId"
1914
2244
  responses:
1915
2245
  "200":
1916
- description: Squad
2246
+ description: Repositories with ai-agent-readiness slice
1917
2247
  content:
1918
2248
  application/json:
1919
2249
  schema:
1920
- $ref: "#/components/schemas/SquadDto"
1921
- "401":
1922
- $ref: "#/components/responses/Unauthorized"
1923
- put:
1924
- tags: [SquadProjects]
1925
- summary: Upsert squad
1926
- operationId: putSquad
1927
- parameters:
1928
- - $ref: "#/components/parameters/WorkspaceId"
1929
- requestBody:
1930
- required: true
1931
- content:
1932
- application/json:
1933
- schema:
1934
- $ref: "#/components/schemas/SquadDto"
1935
- responses:
1936
- "204":
1937
- description: Saved
2250
+ type: array
2251
+ items:
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
1938
2277
  "401":
1939
2278
  $ref: "#/components/responses/Unauthorized"
2279
+ "403":
2280
+ $ref: "#/components/responses/Forbidden"
2281
+ "404":
2282
+ description: Workspace does not exist
2283
+ content:
2284
+ application/json:
2285
+ schema:
2286
+ $ref: "#/components/schemas/ErrorMessageDto"
1940
2287
 
1941
- /workspaces/{workspaceId}/projects:
2288
+ /workspaces/{workspaceId}/agentic-foundation/repository-health:
1942
2289
  get:
1943
- tags: [SquadProjects]
1944
- summary: List projects
1945
- operationId: listProjects
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
1946
2294
  parameters:
1947
2295
  - $ref: "#/components/parameters/WorkspaceId"
1948
2296
  responses:
1949
2297
  "200":
1950
- description: Projects
2298
+ description: Repositories with repo-hygiene slice
1951
2299
  content:
1952
2300
  application/json:
1953
2301
  schema:
1954
2302
  type: array
1955
2303
  items:
1956
- $ref: "#/components/schemas/ProjectDto"
2304
+ $ref: "#/components/schemas/RepoReadinessScoreDto"
1957
2305
  "401":
1958
2306
  $ref: "#/components/responses/Unauthorized"
1959
- post:
1960
- tags: [SquadProjects]
1961
- summary: Create project
1962
- operationId: createProject
1963
- parameters:
1964
- - $ref: "#/components/parameters/WorkspaceId"
1965
- requestBody:
1966
- required: true
1967
- content:
1968
- application/json:
1969
- schema:
1970
- $ref: "#/components/schemas/ProjectDto"
1971
- responses:
1972
- "200":
1973
- description: Created project
2307
+ "403":
2308
+ $ref: "#/components/responses/Forbidden"
2309
+ "404":
2310
+ description: Workspace does not exist
1974
2311
  content:
1975
2312
  application/json:
1976
2313
  schema:
1977
- $ref: "#/components/schemas/ProjectDto"
1978
- "401":
1979
- $ref: "#/components/responses/Unauthorized"
2314
+ $ref: "#/components/schemas/ErrorMessageDto"
1980
2315
 
1981
- /workspaces/{workspaceId}/projects/{projectId}:
1982
- patch:
1983
- tags: [SquadProjects]
1984
- summary: Update project
1985
- operationId: patchProject
2316
+ /workspaces/{workspaceId}/agentic-foundation/codebase-quality:
2317
+ get:
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
1986
2322
  parameters:
1987
2323
  - $ref: "#/components/parameters/WorkspaceId"
1988
- - $ref: "#/components/parameters/ProjectId"
1989
- requestBody:
1990
- required: true
1991
- content:
1992
- application/json:
1993
- schema:
1994
- $ref: "#/components/schemas/ProjectPatchDto"
1995
2324
  responses:
1996
2325
  "200":
1997
- description: Updated project
2326
+ description: Repositories with stack-quality slice
1998
2327
  content:
1999
2328
  application/json:
2000
2329
  schema:
2001
- $ref: "#/components/schemas/ProjectDto"
2330
+ type: array
2331
+ items:
2332
+ $ref: "#/components/schemas/RepoReadinessScoreDto"
2002
2333
  "401":
2003
2334
  $ref: "#/components/responses/Unauthorized"
2335
+ "403":
2336
+ $ref: "#/components/responses/Forbidden"
2337
+ "404":
2338
+ description: Workspace does not exist
2339
+ content:
2340
+ application/json:
2341
+ schema:
2342
+ $ref: "#/components/schemas/ErrorMessageDto"
2004
2343
 
2005
- /workspaces/{workspaceId}/notifications:
2344
+ /workspaces/{workspaceId}/agentic-foundation/test-qa:
2006
2345
  get:
2007
- tags: [Notifications]
2008
- summary: Paginated notifications
2009
- operationId: listNotifications
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
2010
2350
  parameters:
2011
2351
  - $ref: "#/components/parameters/WorkspaceId"
2012
- - name: limit
2013
- in: query
2014
- schema: { type: integer, minimum: 1 }
2015
- - name: offset
2016
- in: query
2017
- schema: { type: integer, minimum: 0 }
2018
2352
  responses:
2019
2353
  "200":
2020
- description: Page of notifications
2354
+ description: Repositories with tests-qa slice
2021
2355
  content:
2022
2356
  application/json:
2023
2357
  schema:
2024
- $ref: "#/components/schemas/NotificationListDto"
2025
- "401":
2026
- $ref: "#/components/responses/Unauthorized"
2027
-
2028
- /workspaces/{workspaceId}/notifications/{id}/read:
2029
- post:
2030
- tags: [Notifications]
2031
- summary: Mark notification read
2032
- operationId: postNotificationRead
2033
- parameters:
2034
- - $ref: "#/components/parameters/WorkspaceId"
2035
- - $ref: "#/components/parameters/NotificationId"
2036
- responses:
2037
- "204":
2038
- description: Updated
2039
- "401":
2040
- $ref: "#/components/responses/Unauthorized"
2041
-
2042
- /workspaces/{workspaceId}/notifications/read-all:
2043
- post:
2044
- tags: [Notifications]
2045
- summary: Mark all notifications read
2046
- operationId: postNotificationsReadAll
2047
- parameters:
2048
- - $ref: "#/components/parameters/WorkspaceId"
2049
- responses:
2050
- "204":
2051
- description: Updated
2358
+ type: array
2359
+ items:
2360
+ $ref: "#/components/schemas/RepoReadinessScoreDto"
2052
2361
  "401":
2053
2362
  $ref: "#/components/responses/Unauthorized"
2054
-
2055
- /workspaces/{workspaceId}/ai-strategy/recommend:
2056
- post:
2057
- tags: [AIStrategy]
2058
- summary: Generate AI strategy recommendations
2059
- operationId: postAiStrategyRecommend
2060
- parameters:
2061
- - $ref: "#/components/parameters/WorkspaceId"
2062
- requestBody:
2063
- required: true
2064
- content:
2065
- application/json:
2066
- schema:
2067
- $ref: "#/components/schemas/AiStrategyRecommendBodyDto"
2068
- responses:
2069
- "200":
2070
- description: Plan
2363
+ "403":
2364
+ $ref: "#/components/responses/Forbidden"
2365
+ "404":
2366
+ description: Workspace does not exist
2071
2367
  content:
2072
2368
  application/json:
2073
2369
  schema:
2074
- $ref: "#/components/schemas/AiStrategyPlanDto"
2075
- "401":
2076
- $ref: "#/components/responses/Unauthorized"
2370
+ $ref: "#/components/schemas/ErrorMessageDto"
2077
2371
 
2078
- /workspaces/{workspaceId}/journey/progress:
2372
+ /workspaces/{workspaceId}/agentic-foundation/observability:
2079
2373
  get:
2080
- tags: [Journey]
2081
- summary: Journey step completion
2082
- operationId: getJourneyProgress
2374
+ tags: [AgenticFoundation]
2375
+ summary: Observability pillar (observability slice)
2376
+ description: Returns per-repo `observability` dimension only (issue #284).
2377
+ operationId: getAgenticFoundationObservability
2083
2378
  parameters:
2084
2379
  - $ref: "#/components/parameters/WorkspaceId"
2085
2380
  responses:
2086
2381
  "200":
2087
- description: Progress (empty completedSteps when no row exists yet; updatedAt is synthetic in that case)
2088
- content:
2089
- application/json:
2090
- schema:
2091
- $ref: "#/components/schemas/JourneyProgressDto"
2092
- "400":
2093
- description: Malformed workspaceId
2382
+ description: Repositories with observability slice
2094
2383
  content:
2095
2384
  application/json:
2096
2385
  schema:
2097
- $ref: "#/components/schemas/ErrorMessageDto"
2386
+ type: array
2387
+ items:
2388
+ $ref: "#/components/schemas/RepoReadinessScoreDto"
2098
2389
  "401":
2099
2390
  $ref: "#/components/responses/Unauthorized"
2100
2391
  "403":
@@ -2105,34 +2396,24 @@ paths:
2105
2396
  application/json:
2106
2397
  schema:
2107
2398
  $ref: "#/components/schemas/ErrorMessageDto"
2108
- put:
2109
- tags: [Journey]
2110
- summary: Replace completed steps
2111
- operationId: putJourneyProgress
2112
- description: >
2113
- Idempotent replace-all of completed route strings. Returns the persisted
2114
- JourneyProgressDto including updatedAt.
2399
+
2400
+ /workspaces/{workspaceId}/agentic-foundation/api-contract:
2401
+ get:
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
2115
2406
  parameters:
2116
2407
  - $ref: "#/components/parameters/WorkspaceId"
2117
- requestBody:
2118
- required: true
2119
- content:
2120
- application/json:
2121
- schema:
2122
- $ref: "#/components/schemas/JourneyProgressUpdateDto"
2123
2408
  responses:
2124
2409
  "200":
2125
- description: Saved progress
2126
- content:
2127
- application/json:
2128
- schema:
2129
- $ref: "#/components/schemas/JourneyProgressDto"
2130
- "400":
2131
- description: Body fails validation (non-array, invalid entries, too many steps)
2410
+ description: Repositories with api-contracts slice
2132
2411
  content:
2133
2412
  application/json:
2134
2413
  schema:
2135
- $ref: "#/components/schemas/ErrorMessageDto"
2414
+ type: array
2415
+ items:
2416
+ $ref: "#/components/schemas/RepoReadinessScoreDto"
2136
2417
  "401":
2137
2418
  $ref: "#/components/responses/Unauthorized"
2138
2419
  "403":
@@ -2144,55 +2425,158 @@ paths:
2144
2425
  schema:
2145
2426
  $ref: "#/components/schemas/ErrorMessageDto"
2146
2427
 
2147
- /me/preferences:
2428
+ /workspaces/{workspaceId}/agentic-foundation/platform-governance:
2148
2429
  get:
2149
- tags: [Preferences]
2150
- summary: Get preferences for current user
2151
- operationId: getMyPreferences
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"
2152
2436
  responses:
2153
2437
  "200":
2154
- description: Preferences
2438
+ description: Repositories with platform-governance slice
2155
2439
  content:
2156
2440
  application/json:
2157
2441
  schema:
2158
- $ref: "#/components/schemas/PreferencesDto"
2442
+ type: array
2443
+ items:
2444
+ $ref: "#/components/schemas/RepoReadinessScoreDto"
2159
2445
  "401":
2160
2446
  $ref: "#/components/responses/Unauthorized"
2161
- put:
2162
- tags: [Preferences]
2163
- summary: Upsert preferences
2164
- operationId: putMyPreferences
2165
- requestBody:
2166
- required: true
2167
- content:
2168
- application/json:
2169
- schema:
2170
- $ref: "#/components/schemas/PreferencesDto"
2447
+ "403":
2448
+ $ref: "#/components/responses/Forbidden"
2449
+ "404":
2450
+ description: Workspace does not exist
2451
+ content:
2452
+ application/json:
2453
+ schema:
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"
2171
2464
  responses:
2172
2465
  "200":
2173
- description: Saved preferences
2466
+ description: Repositories with delivery slice
2174
2467
  content:
2175
2468
  application/json:
2176
2469
  schema:
2177
- $ref: "#/components/schemas/PreferencesDto"
2178
- "400":
2179
- description: Validation failed
2470
+ type: array
2471
+ items:
2472
+ $ref: "#/components/schemas/RepoReadinessScoreDto"
2473
+ "401":
2474
+ $ref: "#/components/responses/Unauthorized"
2475
+ "403":
2476
+ $ref: "#/components/responses/Forbidden"
2477
+ "404":
2478
+ description: Workspace does not exist
2180
2479
  content:
2181
2480
  application/json:
2182
2481
  schema:
2183
- $ref: "#/components/schemas/PreferencesValidationErrorDto"
2482
+ $ref: "#/components/schemas/ErrorMessageDto"
2483
+
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"
2184
2503
  "401":
2185
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"
2186
2513
 
2187
- /workspaces/{workspaceId}/org-chart/import:
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"
2188
2572
  post:
2189
- tags: [OrgChartImport]
2190
- summary: Import org chart from Supabase storage path
2573
+ tags: [RepositoryReadiness]
2574
+ summary: Enqueue fix for an issue
2191
2575
  description: >
2192
- Parse-only. Downloads the CSV at `storagePath` from the `org-charts` bucket and returns
2193
- `persons` and `errors`. Does not persist to `spa_org_chart` or `org_chart`. The SPA
2194
- derives teams client-side and saves the org structure via `POST /workspaces/{workspaceId}/org-chart`.
2195
- 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
2196
2580
  parameters:
2197
2581
  - $ref: "#/components/parameters/WorkspaceId"
2198
2582
  requestBody:
@@ -2200,16 +2584,21 @@ paths:
2200
2584
  content:
2201
2585
  application/json:
2202
2586
  schema:
2203
- $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"
2204
2593
  responses:
2205
2594
  "200":
2206
- description: Parsed persons from uploaded CSV (not persisted server-side)
2595
+ description: Fix request created or existing row returned
2207
2596
  content:
2208
2597
  application/json:
2209
2598
  schema:
2210
- $ref: "#/components/schemas/OrgChartImportResultDto"
2599
+ $ref: "#/components/schemas/FixRequestDto"
2211
2600
  "400":
2212
- description: Invalid body or storage path
2601
+ description: Invalid body or malformed `repoId` (not a UUID)
2213
2602
  content:
2214
2603
  application/json:
2215
2604
  schema:
@@ -2219,55 +2608,415 @@ paths:
2219
2608
  "403":
2220
2609
  $ref: "#/components/responses/Forbidden"
2221
2610
  "404":
2222
- description: Storage object not found
2611
+ description: Workspace not found, or issue/repo pair not in workspace
2223
2612
  content:
2224
2613
  application/json:
2225
2614
  schema:
2226
2615
  $ref: "#/components/schemas/ErrorMessageDto"
2227
- "502":
2228
- 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
2229
2642
  content:
2230
2643
  application/json:
2231
2644
  schema:
2232
2645
  $ref: "#/components/schemas/ErrorMessageDto"
2233
- "503":
2234
- description: Storage import not configured or service unavailable
2646
+ "409":
2647
+ description: Fix is not in a retryable state
2235
2648
  content:
2236
2649
  application/json:
2237
2650
  schema:
2238
2651
  $ref: "#/components/schemas/ErrorMessageDto"
2239
2652
 
2240
- components:
2241
- securitySchemes:
2242
- bearerAuth:
2243
- type: http
2244
- scheme: bearer
2245
- bearerFormat: JWT
2246
- description: Supabase access token (`Authorization` header).
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"
2247
2686
 
2248
- parameters:
2249
- WorkspaceId:
2250
- name: workspaceId
2251
- in: path
2252
- required: true
2253
- schema:
2254
- type: string
2255
- RepoId:
2256
- name: repoId
2257
- in: path
2258
- required: true
2259
- schema:
2260
- type: string
2261
- format: uuid
2262
- ProjectId:
2263
- name: projectId
2264
- in: path
2265
- required: true
2266
- schema:
2267
- type: string
2268
- NotificationId:
2269
- name: id
2270
- in: path
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
2271
3020
  required: true
2272
3021
  schema:
2273
3022
  type: string
@@ -2278,390 +3027,1305 @@ components:
2278
3027
  schema:
2279
3028
  type: string
2280
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
3304
+
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
2281
3611
 
2282
- responses:
2283
- Unauthorized:
2284
- description: Missing or invalid bearer token
2285
- content:
2286
- application/json:
2287
- schema:
2288
- $ref: "#/components/schemas/ErrorMessageDto"
2289
- Forbidden:
2290
- description: Authenticated but not allowed
2291
- content:
2292
- application/json:
2293
- schema:
2294
- $ref: "#/components/schemas/ErrorMessageDto"
3612
+ ProductivityInsightsTeamDetailDto:
3613
+ type: object
3614
+ additionalProperties: false
3615
+ required: [teamId, teamName, headcount, weeks, summary]
3616
+ properties:
3617
+ teamId:
3618
+ type: string
3619
+ teamName:
3620
+ type: string
3621
+ headcount:
3622
+ type: integer
3623
+ minimum: 0
3624
+ managerLabel:
3625
+ type: string
3626
+ weeks:
3627
+ type: array
3628
+ items:
3629
+ $ref: "#/components/schemas/ProductivityInsightsAggregatedTeamWeekDto"
3630
+ summary:
3631
+ $ref: "#/components/schemas/ProductivityInsightsTeamSummaryDto"
2295
3632
 
2296
- schemas:
2297
- ErrorMessageDto:
3633
+ ProductivityInsightsTeamsResponseDto:
2298
3634
  type: object
2299
- required: [error]
3635
+ additionalProperties: false
3636
+ required: [workspaceId, range, scope, generatedAt]
2300
3637
  properties:
2301
- error:
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:
2302
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"
2303
3658
 
2304
- TeamMemberDto:
3659
+ ProductivityInsightsContributorProfileDto:
2305
3660
  type: object
2306
- required: [id, name, email, role]
3661
+ additionalProperties: false
3662
+ required: [id, name]
2307
3663
  properties:
2308
3664
  id:
2309
3665
  type: string
3666
+ description: Org-chart person id (typically email).
2310
3667
  name:
2311
3668
  type: string
2312
- email:
3669
+ title:
2313
3670
  type: string
2314
- role:
3671
+ team:
3672
+ type: string
3673
+ department:
2315
3674
  type: string
2316
- enum: [admin, editor, viewer]
2317
3675
 
2318
- TeamMemberListDto:
3676
+ ProductivityInsightsContributorActivityDayDto:
2319
3677
  type: object
2320
- required: [members]
3678
+ additionalProperties: false
3679
+ required:
3680
+ [date, dayIndex, mergedPrCount, mergedPrs, absent, failureSignal, failures]
2321
3681
  properties:
2322
- 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:
2323
3693
  type: array
2324
3694
  items:
2325
- $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
2326
3706
 
2327
- InviteTeamMemberBodyDto:
3707
+ ProductivityInsightsContributorActivityWeekDto:
2328
3708
  type: object
2329
- required: [email, role]
3709
+ additionalProperties: false
3710
+ required: [label, bucketStart, bucketEnd, days]
2330
3711
  properties:
2331
- email:
3712
+ label:
2332
3713
  type: string
2333
- role:
3714
+ bucketStart:
2334
3715
  type: string
2335
- 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"
2336
3724
 
2337
- UpdateTeamMemberRoleBodyDto:
3725
+ ProductivityInsightsContributorActivitySummaryDto:
2338
3726
  type: object
2339
- required: [role]
3727
+ additionalProperties: false
3728
+ required:
3729
+ - totalMergedPrs
3730
+ - avgMergedPrsPerWeek
3731
+ - totalAbsenceDays
3732
+ - avgFailRate
3733
+ - mergedPrDelta
3734
+ - avgMergedPrsPerWeekDelta
3735
+ - totalAbsenceDaysDelta
3736
+ - avgFailRateDelta
2340
3737
  properties:
2341
- role:
2342
- type: string
2343
- 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
2344
3756
 
2345
- OrgChartNodeDto:
3757
+ ProductivityInsightsContributorDetailDto:
2346
3758
  type: object
2347
3759
  additionalProperties: false
2348
- required: [id, name, type]
3760
+ required:
3761
+ - workspaceId
3762
+ - contributorId
3763
+ - range
3764
+ - scope
3765
+ - generatedAt
3766
+ - contributor
3767
+ - weeks
3768
+ - summary
2349
3769
  properties:
2350
- id:
3770
+ workspaceId:
2351
3771
  type: string
2352
- name:
3772
+ format: uuid
3773
+ contributorId:
2353
3774
  type: string
2354
- type:
3775
+ range:
2355
3776
  type: string
2356
- enum: [org, department, team, person]
2357
- external_ids:
2358
- type: object
2359
- additionalProperties: { type: string }
2360
- 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:
2361
3786
  type: array
2362
3787
  items:
2363
- $ref: "#/components/schemas/OrgChartNodeDto"
3788
+ $ref: "#/components/schemas/ProductivityInsightsContributorActivityWeekDto"
3789
+ summary:
3790
+ $ref: "#/components/schemas/ProductivityInsightsContributorActivitySummaryDto"
2364
3791
 
2365
- OrgChartTreeNodeDto:
3792
+ ProductivityInsightsNeedsAttentionItemDto:
2366
3793
  type: object
2367
- required: [id]
3794
+ additionalProperties: false
3795
+ required:
3796
+ [rank, entityId, entityType, name, periodAvgMergedPrs, deltaVsPeriodAvgPercent]
2368
3797
  properties:
2369
- 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:
2370
3806
  type: string
3807
+ enum: [team, contributor]
2371
3808
  name:
2372
3809
  type: string
2373
- type:
3810
+ subtitle:
2374
3811
  type: string
2375
- 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:
2376
3829
  type: string
2377
- team:
3830
+ format: uuid
3831
+ range:
2378
3832
  type: string
2379
- role:
3833
+ enum: [4w, 8w, q]
3834
+ scope:
3835
+ $ref: "#/components/schemas/ProductivityInsightsAggregatedTeamsScopeDto"
3836
+ entityType:
2380
3837
  type: string
2381
- level:
3838
+ enum: [teams, contributors]
3839
+ generatedAt:
2382
3840
  type: string
2383
- external_ids:
2384
- type: object
2385
- additionalProperties: { type: string }
2386
- children:
3841
+ format: date-time
3842
+ items:
2387
3843
  type: array
2388
3844
  items:
2389
- $ref: "#/components/schemas/OrgChartTreeNodeDto"
3845
+ $ref: "#/components/schemas/ProductivityInsightsNeedsAttentionItemDto"
2390
3846
 
2391
- ScoringOrgChartDto:
3847
+ AiUsageSpendAggregatedTeamWeekDto:
2392
3848
  type: object
2393
3849
  additionalProperties: false
2394
- required: [root]
3850
+ required: [bucketStart, bucketEnd, tokenCount, spendCents, activeUsers]
2395
3851
  properties:
2396
- root:
2397
- $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.
2398
3873
 
2399
- OrgChartDto:
3874
+ AiUsageSpendAggregatedTeamRowDto:
2400
3875
  type: object
2401
3876
  additionalProperties: false
2402
- required: [root]
3877
+ required: [teamId, teamName, headcount, weeks]
2403
3878
  properties:
2404
- root:
2405
- $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"
2406
3892
 
2407
- OrgChartPersonDto:
3893
+ AiUsageSpendAggregatedTeamsSummaryDto:
2408
3894
  type: object
2409
3895
  additionalProperties: false
2410
- required: [employeeId, name, email, title, department, team, managerId, location]
3896
+ required:
3897
+ [totalTokenCount, totalSpendCents, activeUsers, activeTools, totalEngineers]
2411
3898
  properties:
2412
- 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:
2413
3924
  type: string
2414
- name:
3925
+ format: uuid
3926
+ range:
2415
3927
  type: string
2416
- email:
3928
+ enum: [4w, 8w, q]
3929
+ scope:
3930
+ $ref: "#/components/schemas/ProductivityInsightsAggregatedTeamsScopeDto"
3931
+ generatedAt:
2417
3932
  type: string
2418
- 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:
2419
3947
  type: string
2420
- department:
3948
+ description: Org-chart person id (typically email).
3949
+ contributorName:
2421
3950
  type: string
2422
- team:
3951
+ title:
2423
3952
  type: string
2424
- managerId:
3953
+ team:
2425
3954
  type: string
2426
- location:
3955
+ department:
2427
3956
  type: string
2428
-
2429
- OrgChartImportResultDto:
2430
- type: object
2431
- additionalProperties: false
2432
- required: [persons, errors]
2433
- properties:
2434
- persons:
2435
- type: array
2436
- items:
2437
- $ref: "#/components/schemas/OrgChartPersonDto"
2438
- errors:
3957
+ weeks:
2439
3958
  type: array
2440
3959
  items:
2441
- type: string
3960
+ $ref: "#/components/schemas/AiUsageSpendAggregatedTeamWeekDto"
2442
3961
 
2443
- OrgChartSaveResultDto:
3962
+ AiUsageSpendAggregatedContributorsDto:
2444
3963
  type: object
2445
3964
  additionalProperties: false
2446
- required: [ok, tenantId]
3965
+ required: [workspaceId, range, scope, generatedAt, contributors, summary]
2447
3966
  properties:
2448
- ok:
2449
- type: boolean
2450
- tenantId:
3967
+ workspaceId:
2451
3968
  type: string
2452
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:
3979
+ type: array
3980
+ items:
3981
+ $ref: "#/components/schemas/AiUsageSpendAggregatedContributorRowDto"
3982
+ summary:
3983
+ $ref: "#/components/schemas/AiUsageSpendAggregatedTeamsSummaryDto"
2453
3984
 
2454
- TenantSourceEntryDto:
3985
+ AiUsageSpendContributorToolHeatmapDto:
2455
3986
  type: object
2456
- required: [type]
3987
+ additionalProperties: false
3988
+ required:
3989
+ - workspaceId
3990
+ - range
3991
+ - scope
3992
+ - generatedAt
3993
+ - contributors
2457
3994
  properties:
2458
- type:
3995
+ workspaceId:
3996
+ type: string
3997
+ format: uuid
3998
+ range:
2459
3999
  type: string
2460
- 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"
2461
4010
 
2462
- CursorSourceDto:
4011
+ AiUsageSpendKeyInsightItemDto:
2463
4012
  type: object
2464
- description: >
2465
- Cursor (AI Tools) source entry for `PUT /workspaces/{workspaceId}/sources`
2466
- and `POST /workspaces/{workspaceId}/sources/test`. The `token` field
2467
- carries a literal Cursor API key on writes; reads from
2468
- `GET /workspaces/{workspaceId}/config` redact it to `***`.
2469
- Runtime calls use the Cursor Admin API at `https://api.cursor.com` (HTTP Basic); do not rely on `baseUrl`.
2470
- required: [type, token]
2471
4013
  additionalProperties: false
4014
+ required: [id, tone, text]
2472
4015
  properties:
2473
- type:
4016
+ id:
2474
4017
  type: string
2475
- enum: [cursor]
2476
- token:
4018
+ description: Stable insight key (e.g. spend-leader, spend-vs-output).
4019
+ tone:
2477
4020
  type: string
2478
- minLength: 1
2479
- maxLength: 256
2480
- description: Cursor API key (literal). Whitespace and `env:` prefix are rejected.
2481
- baseUrl:
4021
+ enum: [positive, warning, neutral]
4022
+ text:
2482
4023
  type: string
2483
- deprecated: true
2484
- description: >
2485
- Deprecated. Historically suggested an API base URL; the server ignores this field
2486
- and always uses the Cursor Admin API host. Kept so older clients can submit payloads unchanged.
2487
- default: "https://api.cursor.com"
4024
+ description: Human-readable insight sentence for the UI panel.
2488
4025
 
2489
- WorkspaceConfigDto:
4026
+ AiUsageSpendKeyInsightsDto:
2490
4027
  type: object
2491
- required: [sources]
4028
+ additionalProperties: false
4029
+ required: [workspaceId, range, scope, generatedAt, items]
2492
4030
  properties:
2493
- 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:
2494
4043
  type: array
2495
4044
  items:
2496
- $ref: "#/components/schemas/TenantSourceEntryDto"
2497
- orgChart:
2498
- nullable: true
2499
- allOf:
2500
- - $ref: "#/components/schemas/ScoringOrgChartDto"
2501
- region:
2502
- type: string
2503
- nullable: true
2504
- enum: [us, eu, me]
4045
+ $ref: "#/components/schemas/AiUsageSpendKeyInsightItemDto"
2505
4046
 
2506
- TestConnectionResponseDto:
2507
- type: object
2508
- required: [success]
2509
- properties:
2510
- success:
2511
- type: boolean
2512
- error:
2513
- 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.
2514
4054
 
2515
- AiToolSeatDto:
4055
+ AiUsageSpendAiAssistedCodeWeekDto:
2516
4056
  type: object
2517
- 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
+ ]
2518
4068
  properties:
2519
- externalId:
4069
+ bucketStart:
2520
4070
  type: string
2521
- email:
2522
- type: string
2523
- displayName:
4071
+ format: date-time
4072
+ bucketEnd:
2524
4073
  type: string
2525
- nullable: true
2526
- role:
4074
+ format: date-time
4075
+ weekLabel:
2527
4076
  type: string
2528
- nullable: true
2529
- isRemoved:
2530
- type: boolean
2531
- payload:
2532
- type: object
2533
- 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"
2534
4092
 
2535
- AiToolDailyUsageRowDto:
4093
+ AiUsageSpendAiAssistedCodePercentageDto:
2536
4094
  type: object
2537
- 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
2538
4108
  properties:
2539
- day:
2540
- type: string
2541
- format: date
2542
- externalId:
4109
+ workspaceId:
2543
4110
  type: string
2544
- email:
4111
+ format: uuid
4112
+ range:
2545
4113
  type: string
2546
- nullable: true
2547
- isActive:
2548
- type: boolean
2549
- mostUsedModel:
4114
+ enum: [4w, 8w, q]
4115
+ scope:
4116
+ $ref: "#/components/schemas/ProductivityInsightsAggregatedTeamsScopeDto"
4117
+ generatedAt:
2550
4118
  type: string
2551
- nullable: true
2552
- payload:
2553
- type: object
2554
- 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"
2555
4153
 
2556
- AiToolSpendRowDto:
4154
+ AiUsageSpendAiToolUsageSpendProviderItemDto:
2557
4155
  type: object
2558
- required: [billingCycleStartMs, externalId, spendCents]
4156
+ additionalProperties: false
4157
+ required: [providerId, providerName, tokenCount, spendCents, activeUsers]
2559
4158
  properties:
2560
- billingCycleStartMs:
2561
- type: integer
2562
- format: int64
2563
- externalId:
4159
+ providerId:
2564
4160
  type: string
2565
- email:
4161
+ providerName:
2566
4162
  type: string
2567
- nullable: true
4163
+ tokenCount:
4164
+ type: integer
4165
+ minimum: 0
2568
4166
  spendCents:
2569
4167
  type: integer
2570
- format: int64
2571
- payload:
2572
- type: object
2573
- additionalProperties: true
4168
+ minimum: 0
4169
+ activeUsers:
4170
+ type: integer
4171
+ minimum: 0
2574
4172
 
2575
- AiToolRefreshStatusDto:
4173
+ AiUsageSpendAiToolUsageSpendModelItemDto:
2576
4174
  type: object
2577
- required: [lastAttemptAt, counts]
4175
+ additionalProperties: false
4176
+ required: [providerId, providerName, modelName, tokenCount, spendCents]
2578
4177
  properties:
2579
- lastAttemptAt:
2580
- type: string
2581
- format: date-time
2582
- lastSuccessAt:
4178
+ providerId:
2583
4179
  type: string
2584
- format: date-time
2585
- nullable: true
2586
- lastErrorKind:
4180
+ providerName:
2587
4181
  type: string
2588
- nullable: true
2589
- lastErrorMessage:
4182
+ modelName:
2590
4183
  type: string
2591
- nullable: true
2592
- durationMs:
4184
+ tokenCount:
2593
4185
  type: integer
2594
- format: int64
2595
- nullable: true
2596
- counts:
2597
- type: object
2598
- additionalProperties: true
4186
+ minimum: 0
4187
+ spendCents:
4188
+ type: integer
4189
+ minimum: 0
2599
4190
 
2600
- AiToolRefreshResponseDto:
4191
+ AiUsageSpendAiToolUsageSpendDto:
2601
4192
  type: object
2602
- required: [ok]
4193
+ additionalProperties: false
4194
+ required:
4195
+ - workspaceId
4196
+ - range
4197
+ - scope
4198
+ - generatedAt
4199
+ - totalEngineers
4200
+ - providers
4201
+ - models
2603
4202
  properties:
2604
- ok:
2605
- type: boolean
2606
- error:
4203
+ workspaceId:
2607
4204
  type: string
2608
- nullable: true
2609
-
2610
- DashboardProductivityMetricsDto:
2611
- type: object
2612
- description: Nested metric groups; see TypeScript `DashboardProductivityMetricsDto` in @thinkai/tai-api-contract.
2613
- 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"
2614
4225
 
2615
- DashboardProductivityDto:
4226
+ AiUsageSpendTeamToolHeatmapDto:
2616
4227
  type: object
2617
- required: [workspaceId, windowStart, windowEnd, scope, metrics]
4228
+ additionalProperties: false
4229
+ required:
4230
+ - workspaceId
4231
+ - range
4232
+ - scope
4233
+ - generatedAt
4234
+ - teams
2618
4235
  properties:
2619
4236
  workspaceId:
2620
4237
  type: string
2621
4238
  format: uuid
4239
+ range:
4240
+ type: string
4241
+ enum: [4w, 8w, q]
4242
+ scope:
4243
+ $ref: "#/components/schemas/ProductivityInsightsAggregatedTeamsScopeDto"
2622
4244
  generatedAt:
2623
4245
  type: string
2624
4246
  format: date-time
2625
- description: Response generation time (RFC 3339).
2626
- 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:
2627
4258
  type: string
2628
4259
  format: date-time
2629
- windowEnd:
4260
+ bucketEnd:
2630
4261
  type: string
2631
4262
  format: date-time
2632
- scope:
4263
+ weekLabel:
2633
4264
  type: string
2634
- enum: [org, team, person]
2635
- metrics:
2636
- $ref: "#/components/schemas/DashboardProductivityMetricsDto"
2637
- meta:
2638
- type: object
2639
- 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.
2640
4273
 
2641
- DashboardProductivityHistoryDto:
4274
+ AiUsageSpendRoiProductivitySignalDto:
2642
4275
  type: object
2643
- 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
2644
4290
  properties:
2645
4291
  workspaceId:
2646
4292
  type: string
2647
4293
  format: uuid
2648
- bucket:
4294
+ range:
2649
4295
  type: string
2650
- enum: [day, week, month]
2651
- 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:
2652
4326
  type: array
2653
4327
  items:
2654
- type: object
2655
- required: [bucketStart, bucketEnd, metrics]
2656
- properties:
2657
- bucketStart:
2658
- type: string
2659
- format: date-time
2660
- bucketEnd:
2661
- type: string
2662
- format: date-time
2663
- metrics:
2664
- $ref: "#/components/schemas/DashboardProductivityMetricsDto"
4328
+ $ref: "#/components/schemas/AiUsageSpendRoiProductivitySignalWeekDto"
2665
4329
 
2666
4330
  MeProfileDto:
2667
4331
  type: object