Compare commits

...

21 Commits

Author SHA1 Message Date
7c795cc5bf Expose Session Coach agent summary
All checks were successful
Tests / Tests (pull_request) Successful in 14s
2026-07-22 15:13:17 -07:00
17a518429a Merge pull request 'Add Session Coach generation operations' (#303) from loewy/session-coach-agent-deploy into master
Reviewed-on: #303
2026-07-22 19:25:51 +00:00
33eb213cc9 Add Session Coach generation operations
All checks were successful
Tests / Tests (pull_request) Successful in 3m24s
2026-07-22 11:41:33 -07:00
3b66cb3270 Make video frame rate nullable 2026-07-22 02:20:15 -07:00
d67fdd9fee Merge pull request 'Speed up Shot Lab video source queries' (#304) from codex/fix-shot-lab-video-picker into master
Reviewed-on: #304
2026-07-22 01:08:33 +00:00
d787e5e9a2 Speed up Shot Lab video source queries
All checks were successful
Tests / Tests (pull_request) Successful in 39s
2026-07-21 17:39:55 -07:00
1d73b2d67b Merge pull request 'Declare taxonomy filter variables on the makes/runs leaderboards' (#302) from dean/leaderboard-filter-operations into master
Reviewed-on: #302
2026-07-21 16:59:34 +00:00
Dean Wenstrand
aa731b85fc Declare taxonomy filter variables on the makes/runs leaderboards
All checks were successful
Tests / Tests (pull_request) Successful in 18s
The backend leaderboard resolvers gained game_type, table size and pocket
size filters, but GetMakesLeaderboard and GetRunsLeaderboard never declared
them — GraphQL silently drops undeclared variables, so anything the client
passed was ignored and both boards stayed global.

Declare and forward the five filter args on both queries, matching the
names the drill run leaderboard already uses.
2026-07-21 09:30:37 -07:00
644f70c3b0 Merge pull request 'Shot Lab: video-first GetShotLabVideos (replaces GetRecordedStreams/GetLiveStreams)' (#301) from colonelpanic/shot-lab-video-picker into master 2026-07-21 06:47:45 +00:00
1a95439ed2 Replace GetRecordedStreams/GetLiveStreams with video-first GetShotLabVideos
All checks were successful
Tests / Tests (pull_request) Successful in 44s
Shot Lab's source picker becomes video-first: one query for recent videos
regardless of stream completion (an in-progress recording with no shots
yet still appears), carrying stream recency fields so the client can hint
"actively updating" without gating on it. Operation-only change; schema
is untouched (the stream fields already exist).

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-07-20 23:42:43 -07:00
5e14cd2201 Merge pull request 'Regenerate schema for leaderboard taxonomy filters' (#300) from dean/leaderboard-taxonomy into master
Reviewed-on: #300
2026-07-21 04:20:57 +00:00
Dean Wenstrand
9ec52d9d94 Regenerate schema for leaderboard taxonomy filters
All checks were successful
Tests / Tests (pull_request) Successful in 11s
Adds the optional taxonomy args to getLongestRunsLeaderboard and
getMakesLeaderboard (gameType, rankedOnly, excludeDrills, and
table/pocket size bounds) generated from the backend resolvers.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-07-20 21:19:39 -07:00
4576b25dae Merge pull request 'Regenerate schema with upstream Strawberry' (#299) from codex/strawberry-upgrade into master 2026-07-20 22:08:35 +00:00
650b3bc13c Regenerate schema with upstream Strawberry
All checks were successful
Tests / Tests (pull_request) Successful in 11s
2026-07-20 14:46:00 -07:00
6d9cb4126e Merge pull request 'Add per-video HomographyBackendGQL selection to VideoMetadataInput' (#298) from colonelpanic/homography-backend-selection into master 2026-07-18 02:12:18 +00:00
feeb5b7038 Add HomographyBackendGQL enum and VideoMetadataInput.homographyBackend
All checks were successful
Tests / Tests (pull_request) Successful in 11s
Regenerated from backend Python: adds the HomographyBackendGQL enum
(CLASSIC/TABLENET) and the optional homographyBackend field on
VideoMetadataInput, for per-video homography backend selection.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-07-17 18:26:53 -07:00
3340314166 Merge pull request 'Add per-session Coach GraphQL contract' (#297) from loewy/session-coach-v1 into master
Reviewed-on: #297
2026-07-17 18:33:58 +00:00
6b8fdd4262 Add per-session Coach GraphQL contract
All checks were successful
Tests / Tests (pull_request) Successful in 10s
2026-07-16 16:45:55 -07:00
bfaed33c89 Merge pull request 'feed: select uploadCompletionCursor on VideoCardFields stream' (#296) from dean/feed-upload-completion-cursor into master
Reviewed-on: #296
2026-07-14 18:24:54 +00:00
Dean Wenstrand
1ce3aaed1b feed: select uploadCompletionCursor on VideoCardFields stream
All checks were successful
Tests / Tests (pull_request) Successful in 24s
Lets the client distinguish a finalized-but-not-fully-uploaded stream
(is_completed set, but received cursor short of last_intended_segment_bound)
from one that is genuinely processing.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
2026-07-14 11:15:41 -07:00
1bd03f2bbe Merge pull request 'dean/unify-shotlab-cohorts' (#295) from dean/unify-shotlab-cohorts into master
Reviewed-on: #295
2026-07-13 01:08:48 +00:00
7 changed files with 2550 additions and 1790 deletions

File diff suppressed because it is too large Load Diff

View File

@@ -41,6 +41,7 @@ fragment VideoCardFields on VideoGQL {
stream {
id
lastIntendedSegmentBound
uploadCompletionCursor
streamSegmentType
isCompleted
lastSegmentUploadedAt

View File

@@ -1,5 +1,21 @@
query GetMakesLeaderboard($interval: TimeInterval, $when: DateTime) {
getMakesLeaderboard(interval: $interval, when: $when) {
query GetMakesLeaderboard(
$interval: TimeInterval
$when: DateTime
$gameType: String
$tableSizeMin: Float
$tableSizeMax: Float
$pocketSizeMin: Float
$pocketSizeMax: Float
) {
getMakesLeaderboard(
interval: $interval
when: $when
gameType: $gameType
tableSizeMin: $tableSizeMin
tableSizeMax: $tableSizeMax
pocketSizeMin: $pocketSizeMin
pocketSizeMax: $pocketSizeMax
) {
entries {
user {
id
@@ -13,8 +29,24 @@ query GetMakesLeaderboard($interval: TimeInterval, $when: DateTime) {
}
}
query GetRunsLeaderboard($interval: TimeInterval, $when: DateTime) {
getLongestRunsLeaderboard(interval: $interval, when: $when) {
query GetRunsLeaderboard(
$interval: TimeInterval
$when: DateTime
$gameType: String
$tableSizeMin: Float
$tableSizeMax: Float
$pocketSizeMin: Float
$pocketSizeMax: Float
) {
getLongestRunsLeaderboard(
interval: $interval
when: $when
gameType: $gameType
tableSizeMin: $tableSizeMin
tableSizeMax: $tableSizeMax
pocketSizeMin: $pocketSizeMin
pocketSizeMax: $pocketSizeMax
) {
entries {
id
runLength

View File

@@ -1,34 +0,0 @@
# Lightweight listing of live (in-progress) streams for pickers like the
# Shot Lab "follow a live stream" flow. Selects only what a picker card
# needs; intentionally avoids the heavy VideoCardFields fragment.
query GetLiveStreams($limit: Int! = 25) {
getFeedVideos(
limit: $limit
filters: { isStreamCompleted: false, requireCursorCompletion: false }
includePrivate: MINE
feedInput: { allUsers: true }
) {
videos {
id
name
startTime
createdAt
screenshotUri
elapsedTime
owner {
id
username
profileImageUri
}
stream {
id
isCompleted
lastSegmentUploadedAt
}
}
pageInfo {
hasNextPage
endCursor
}
}
}

View File

@@ -1,10 +1,12 @@
# Lightweight two-step picker data for loading a recorded shot into Shot Lab.
query GetRecordedStreams($limit: Int! = 25) {
# Lightweight two-step picker data for Shot Lab's video-first source: the
# caller's recent videos (completed or still uploading), then that video's
# shots. The stream fields hint at "actively updating" without gating.
query GetShotLabVideos($userId: Int!, $limit: Int! = 25) {
getFeedVideos(
limit: $limit
filters: { isStreamCompleted: true, excludeVideosWithNoShots: true }
includeCallersVideos: false
includePrivate: MINE
feedInput: { allUsers: true }
feedInput: { userId: $userId }
) {
videos {
id
@@ -18,6 +20,11 @@ query GetRecordedStreams($limit: Int! = 25) {
username
profileImageUri
}
stream {
id
isCompleted
lastSegmentUploadedAt
}
}
}
}
@@ -36,26 +43,11 @@ query GetRecordedStreamShots($videoId: Int!, $limit: Int! = 200) {
serializedShotPaths {
b64EncodedBuffer
}
cueObjectFeatures {
cueObjectDistance
cueObjectAngle
cueBallSpeed
shotDirection
spinType
}
pocketingIntentionFeatures {
make
targetPocketDistance
targetPocketAngle
targetPocketAngleDirection
marginOfErrorInDegrees
intendedPocketType
difficulty
}
pocketingIntentionInfo {
ballId
pocketId
pathMetadataIndex
}
}
}

View File

@@ -0,0 +1,100 @@
query GetSessionCoach($videoId: Int!) {
getSessionCoach(videoId: $videoId) {
videoId
processingId
analysisVersion
state
generatedAt
processingStatus
ineligibilityReason
summary
shotCount
madeCount
missedCount
unknownOutcomeCount
analyzedShotCount
makePercentage
primaryCandidate {
...SessionCoachCandidateFields
}
candidates {
...SessionCoachCandidateFields
}
agentGeneration {
...SessionCoachGenerationFields
}
}
}
mutation RequestSessionCoachGeneration($videoId: Int!) {
requestSessionCoachGeneration(videoId: $videoId) {
state
agentGeneration {
...SessionCoachGenerationFields
}
}
}
fragment SessionCoachCandidateFields on SessionCoachCandidateGQL {
id
rank
family
kind
title
body
action
supportingShotIds
drillId
limitations
evidence {
bucketKey
bucketLabel
attemptCount
madeCount
missedCount
makePercentage
comparisonAttemptCount
comparisonMadeCount
comparisonMakePercentage
patternShotCount
score
rankReason
}
}
fragment SessionCoachGenerationFields on SessionCoachGenerationGQL {
id
state
createdAt
completedAt
decision {
selectedCandidateId
sessionSummary
title
body
suggestedAction
drillId
drillReason
practiceAttemptCount
practiceTarget
}
diagnostics {
analysisVersion
promptVersion
providerKey
modelName
inputHash
attemptCount
maxAttempts
enqueueCount
latencyMs
inputTokens
cachedInputTokens
outputTokens
failureCode
updatedAt
startedAt
lastEnqueuedAt
abstentionReason
}
}

File diff suppressed because it is too large Load Diff