@vantageos/backend-doctor 0.1.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (108) hide show
  1. package/README.md +88 -0
  2. package/dist/cli.d.ts +2 -0
  3. package/dist/cli.js +94 -0
  4. package/dist/contract.d.ts +18 -0
  5. package/dist/contract.js +29 -0
  6. package/dist/coverage-cli.d.ts +2 -0
  7. package/dist/coverage-cli.js +17 -0
  8. package/dist/coverage.d.ts +32 -0
  9. package/dist/coverage.js +77 -0
  10. package/dist/detectors/oracle-axis1.d.ts +86 -0
  11. package/dist/detectors/oracle-axis1.js +221 -0
  12. package/dist/detectors/oracle-axis2.d.ts +22 -0
  13. package/dist/detectors/oracle-axis2.js +149 -0
  14. package/dist/detectors/predicates.d.ts +19 -0
  15. package/dist/detectors/predicates.js +334 -0
  16. package/dist/detectors/shared.d.ts +56 -0
  17. package/dist/detectors/shared.js +103 -0
  18. package/dist/detectors/source/_scan.d.ts +43 -0
  19. package/dist/detectors/source/_scan.js +82 -0
  20. package/dist/detectors/source/backfill.d.ts +27 -0
  21. package/dist/detectors/source/backfill.js +211 -0
  22. package/dist/detectors/source/contract.d.ts +33 -0
  23. package/dist/detectors/source/contract.js +385 -0
  24. package/dist/detectors/source/identity.d.ts +47 -0
  25. package/dist/detectors/source/identity.js +410 -0
  26. package/dist/detectors/source/jobs.d.ts +10 -0
  27. package/dist/detectors/source/jobs.js +209 -0
  28. package/dist/detectors/source/lifecycle.d.ts +6 -0
  29. package/dist/detectors/source/lifecycle.js +147 -0
  30. package/dist/detectors/source/readbudget.d.ts +6 -0
  31. package/dist/detectors/source/readbudget.js +133 -0
  32. package/dist/detectors/source/refusal.d.ts +6 -0
  33. package/dist/detectors/source/refusal.js +125 -0
  34. package/dist/detectors/source/security.d.ts +37 -0
  35. package/dist/detectors/source/security.js +392 -0
  36. package/dist/detectors/source/writesafety.d.ts +14 -0
  37. package/dist/detectors/source/writesafety.js +345 -0
  38. package/dist/detectors/standard/_doc.d.ts +66 -0
  39. package/dist/detectors/standard/_doc.js +152 -0
  40. package/dist/detectors/standard/r41-frontier.d.ts +58 -0
  41. package/dist/detectors/standard/r41-frontier.js +176 -0
  42. package/dist/detectors/standard/r46-returns-validator.d.ts +61 -0
  43. package/dist/detectors/standard/r46-returns-validator.js +474 -0
  44. package/dist/detectors/standard/standard-doc.d.ts +12 -0
  45. package/dist/detectors/standard/standard-doc.js +271 -0
  46. package/dist/detectors/structural.d.ts +14 -0
  47. package/dist/detectors/structural.js +83 -0
  48. package/dist/engine-types.d.ts +10 -0
  49. package/dist/engine-types.js +1 -0
  50. package/dist/not-checkable.d.ts +22 -0
  51. package/dist/not-checkable.js +37 -0
  52. package/dist/oracle.d.ts +33 -0
  53. package/dist/oracle.js +133 -0
  54. package/dist/registry.d.ts +11 -0
  55. package/dist/registry.js +140 -0
  56. package/dist/report.d.ts +35 -0
  57. package/dist/report.js +138 -0
  58. package/dist/severity.d.ts +30 -0
  59. package/dist/severity.js +121 -0
  60. package/dist/tally.d.ts +57 -0
  61. package/dist/tally.js +152 -0
  62. package/dist/trigger.d.ts +47 -0
  63. package/dist/trigger.js +143 -0
  64. package/fixtures/backend-standard-51ec7af1.md +648 -0
  65. package/fixtures/backend-standard-a9d7877b.md +629 -0
  66. package/fixtures/mcp-app-standard-a74a042b.md +279 -0
  67. package/fixtures/mcp-extension-standard-a74a042b.md +259 -0
  68. package/fixtures/mcp-server-standard-a74a042b.md +434 -0
  69. package/fixtures/mcp-standard-a74a042b.md +354 -0
  70. package/fixtures/vp-1200-green/.pin +1 -0
  71. package/fixtures/vp-1200-green/convex/migrations.ts +350 -0
  72. package/fixtures/vp-1200-green/convex/schema.ts +1243 -0
  73. package/fixtures/vp-1200-red/.pin +1 -0
  74. package/fixtures/vp-1200-red/convex/migrations.ts +239 -0
  75. package/fixtures/vp-1200-red/convex/schema.ts +1243 -0
  76. package/fixtures/vp-by-tool-day158.csv +190 -0
  77. package/fixtures/vp-convex/.github/workflows/npm-publish.yml +163 -0
  78. package/fixtures/vp-convex/.pin +1 -0
  79. package/fixtures/vp-convex/bun.lock +783 -0
  80. package/fixtures/vp-convex/convex/auth.config.ts +8 -0
  81. package/fixtures/vp-convex/convex/briefingNotes.ts +321 -0
  82. package/fixtures/vp-convex/convex/convex.config.ts +14 -0
  83. package/fixtures/vp-convex/convex/credentials.ts +591 -0
  84. package/fixtures/vp-convex/convex/crons.ts +72 -0
  85. package/fixtures/vp-convex/convex/dashboard.ts +199 -0
  86. package/fixtures/vp-convex/convex/gumroadWebhook.ts +815 -0
  87. package/fixtures/vp-convex/convex/issues.ts +544 -0
  88. package/fixtures/vp-convex/convex/kb.ts +305 -0
  89. package/fixtures/vp-convex/convex/kbMutations.ts +192 -0
  90. package/fixtures/vp-convex/convex/kbShared.ts +56 -0
  91. package/fixtures/vp-convex/convex/licenses.ts +443 -0
  92. package/fixtures/vp-convex/convex/memories.ts +406 -0
  93. package/fixtures/vp-convex/convex/memoriesScoped.ts +220 -0
  94. package/fixtures/vp-convex/convex/messages.ts +971 -0
  95. package/fixtures/vp-convex/convex/migrations.ts +239 -0
  96. package/fixtures/vp-convex/convex/missionTemplates.ts +369 -0
  97. package/fixtures/vp-convex/convex/oauth.ts +1608 -0
  98. package/fixtures/vp-convex/convex/oauthMigrations.ts +96 -0
  99. package/fixtures/vp-convex/convex/okfBundleNode.ts +1031 -0
  100. package/fixtures/vp-convex/convex/recurringTasks.ts +386 -0
  101. package/fixtures/vp-convex/convex/schema.ts +1229 -0
  102. package/fixtures/vp-convex/convex/tasks.ts +2782 -0
  103. package/fixtures/vp-r31-green/.pin +1 -0
  104. package/fixtures/vp-r31-green/convex/migrations.ts +350 -0
  105. package/fixtures/vp-r31-red/.pin +1 -0
  106. package/fixtures/vp-r31-red/convex/migrations.ts +286 -0
  107. package/fixtures/vr-by-tool-day159.csv +89 -0
  108. package/package.json +39 -0
@@ -0,0 +1,199 @@
1
+ import { v } from "convex/values";
2
+ import { query } from "./_generated/server";
3
+ import { withOrgScope, filterByOrgScope, requireScope } from "./lib/auth";
4
+
5
+ // ─────────────────────────────────────────────────────────────────────────────
6
+ // getDashboardSummary — high-level operational stats for VantagePeers dashboard
7
+ // Returns: tasks in progress, active orchestrator profiles, unread message count,
8
+ // open mandate count, and the 20 most recent cross-entity activity events.
9
+ // ─────────────────────────────────────────────────────────────────────────────
10
+
11
+ export const getDashboardSummary = query({
12
+ args: {},
13
+ returns: v.object({
14
+ tasksInProgress: v.number(),
15
+ activeOrchestrators: v.array(
16
+ v.object({
17
+ _id: v.id("profiles"),
18
+ _creationTime: v.number(),
19
+ orchestratorId: v.string(),
20
+ instanceId: v.optional(v.string()),
21
+ name: v.string(),
22
+ static: v.object({
23
+ role: v.string(),
24
+ workspace: v.string(),
25
+ capabilities: v.array(v.string()),
26
+ }),
27
+ dynamic: v.object({
28
+ currentTask: v.optional(v.string()),
29
+ lastSeen: v.number(),
30
+ sessionCount: v.number(),
31
+ }),
32
+ }),
33
+ ),
34
+ unreadMessages: v.number(),
35
+ openMandates: v.number(),
36
+ recentActivity: v.array(
37
+ v.object({
38
+ type: v.union(
39
+ v.literal("task"),
40
+ v.literal("message"),
41
+ v.literal("mandate"),
42
+ ),
43
+ id: v.string(),
44
+ actor: v.string(),
45
+ excerpt: v.string(),
46
+ status: v.optional(v.string()),
47
+ updatedAt: v.number(),
48
+ }),
49
+ ),
50
+ }),
51
+ handler: async (ctx) => {
52
+ // ── Beta multi-tenant scope gate ─────────────────────────────────────
53
+ // Master scope (no org): full dashboard, all orchestrators.
54
+ // Client org: filtered to their allowedOrchestrators.
55
+ const scope = await withOrgScope(ctx);
56
+ // getDashboardSummary requires either master or aggregated stats scope.
57
+ if (!scope.isMaster) {
58
+ requireScope(scope, "view-stats-aggregated");
59
+ }
60
+
61
+ // Tasks in progress — uses index, bounded
62
+ const inProgressTasksAll = await ctx.db
63
+ .query("tasks")
64
+ .withIndex("by_status", (q) => q.eq("status", "in_progress"))
65
+ .take(500);
66
+ const inProgressTasks = filterByOrgScope(inProgressTasksAll, scope);
67
+
68
+ // Open mandates — filter in memory (small table)
69
+ const allMandates = await ctx.db.query("mandates").take(500);
70
+ const openMandates = allMandates.filter((m) => m.status !== "settled").length;
71
+
72
+ // All profiles (small table — one row per instance)
73
+ const profiles = await ctx.db.query("profiles").collect();
74
+
75
+ // Unread messages — full scan bounded at 1000 (receipts table is small)
76
+ // Index is composite [recipient, readAt] so we cannot filter by readAt alone.
77
+ const allReceipts = await ctx.db.query("messageReceipts").take(1000);
78
+ const unreadMessages = allReceipts.filter((r) => r.readAt === undefined).length;
79
+
80
+ // Recent activity — fetch bounded slices of each entity
81
+ // Fetch more tasks before filtering so client-orgs still get 20 items.
82
+ const recentTasksAll = await ctx.db.query("tasks").order("desc").take(100);
83
+ const recentTasks = filterByOrgScope(recentTasksAll, scope).slice(0, 20);
84
+ const recentMessages = await ctx.db.query("messages").order("desc").take(20);
85
+ const recentMandates = await ctx.db.query("mandates").order("desc").take(20);
86
+
87
+ type ActivityEvent = {
88
+ type: "task" | "message" | "mandate";
89
+ id: string;
90
+ actor: string;
91
+ excerpt: string;
92
+ status?: string;
93
+ updatedAt: number;
94
+ };
95
+
96
+ const events: ActivityEvent[] = [
97
+ ...recentTasks.map((t) => ({
98
+ type: "task" as const,
99
+ id: t._id as string,
100
+ actor: t.assignedTo,
101
+ excerpt: t.title,
102
+ status: t.status,
103
+ updatedAt: t.updatedAt,
104
+ })),
105
+ ...recentMessages.map((m) => ({
106
+ type: "message" as const,
107
+ id: m._id as string,
108
+ actor: m.from,
109
+ excerpt: m.content.slice(0, 80),
110
+ updatedAt: m.createdAt,
111
+ })),
112
+ ...recentMandates.map((m) => ({
113
+ type: "mandate" as const,
114
+ id: m._id as string,
115
+ actor: m.requestedBy,
116
+ excerpt: m.service.slice(0, 50),
117
+ status: m.status,
118
+ updatedAt: m.updatedAt,
119
+ })),
120
+ ];
121
+
122
+ events.sort((a, b) => b.updatedAt - a.updatedAt);
123
+
124
+ return {
125
+ tasksInProgress: inProgressTasks.length,
126
+ activeOrchestrators: profiles,
127
+ unreadMessages,
128
+ openMandates,
129
+ recentActivity: events.slice(0, 20),
130
+ };
131
+ },
132
+ });
133
+
134
+ // ─────────────────────────────────────────────────────────────────────────────
135
+ // getProjectSummary — per-project task breakdown + mission counts
136
+ // Returns one entry per unique project name found across tasks and missions.
137
+ // ─────────────────────────────────────────────────────────────────────────────
138
+
139
+ export const getProjectSummary = query({
140
+ args: {},
141
+ returns: v.array(
142
+ v.object({
143
+ name: v.string(),
144
+ missionCount: v.number(),
145
+ tasksByStatus: v.object({
146
+ todo: v.number(),
147
+ in_progress: v.number(),
148
+ review: v.number(),
149
+ blocked: v.number(),
150
+ done: v.number(),
151
+ }),
152
+ activeOrchestrators: v.array(v.string()),
153
+ }),
154
+ ),
155
+ handler: async (ctx) => {
156
+ // ── Beta multi-tenant scope gate ─────────────────────────────────────
157
+ const scope = await withOrgScope(ctx);
158
+ if (!scope.isMaster) {
159
+ requireScope(scope, "view-stats-aggregated");
160
+ }
161
+
162
+ const allTasks = await ctx.db.query("tasks").take(1000);
163
+ const allMissions = await ctx.db.query("missions").take(1000);
164
+ const tasks = filterByOrgScope(allTasks, scope);
165
+ const missions = filterByOrgScope(allMissions, scope);
166
+
167
+ const projectNames = new Set<string>();
168
+ for (const t of tasks) {
169
+ if (t.project !== undefined) projectNames.add(t.project);
170
+ }
171
+ for (const m of missions) {
172
+ if (m.project !== undefined) projectNames.add(m.project);
173
+ }
174
+
175
+ return Array.from(projectNames).map((project) => {
176
+ const projectTasks = tasks.filter((t) => t.project === project);
177
+ const projectMissions = missions.filter((m) => m.project === project);
178
+
179
+ return {
180
+ name: project,
181
+ missionCount: projectMissions.length,
182
+ tasksByStatus: {
183
+ todo: projectTasks.filter((t) => t.status === "todo").length,
184
+ in_progress: projectTasks.filter((t) => t.status === "in_progress").length,
185
+ review: projectTasks.filter((t) => t.status === "review").length,
186
+ blocked: projectTasks.filter((t) => t.status === "blocked").length,
187
+ done: projectTasks.filter((t) => t.status === "done").length,
188
+ },
189
+ activeOrchestrators: [
190
+ ...new Set(
191
+ projectTasks
192
+ .filter((t) => t.status !== "done")
193
+ .map((t) => t.assignedTo),
194
+ ),
195
+ ],
196
+ };
197
+ });
198
+ },
199
+ });