@@ -16,16 +16,7 @@ const PROVIDER_SECRET_BY_NAME = {
1616 anthropic : "ANTHROPIC_API_KEY" ,
1717 openai : "OPENAI_API_KEY"
1818} ;
19- const INFERENCE_PROVIDERS = Object . keys ( PROVIDER_SECRET_BY_NAME ) ;
20- const INFERENCE_PROVIDER_SEED_OFFSET = 4 ;
21- const COPILOT_SECRET_PRIMARY_MODULO = 5 ;
22- const COPILOT_SECRET_SECONDARY_MODULO = 7 ;
23- const THIRD_PARTY_SECRET_BEGINNER_MODULO = 3 ;
24- const THIRD_PARTY_SECRET_SECONDARY_MODULO = 8 ;
25- const ANTHROPIC_MISSING_REMAINDER = 0 ;
26- const OPENAI_MISSING_REMAINDER = 1 ;
27- const COPILOT_PERMISSION_GITHUB_MODULO = 4 ;
28- const COPILOT_PERMISSION_OTHER_MODULO = 3 ;
19+ const INFERENCE_PROVIDERS = [ "github" ] ;
2920const WORD_COUNT_COMPLEXITY_THRESHOLD = 1400 ;
3021const COMMAND_LINE_COMPLEXITY_THRESHOLD = 18 ;
3122const CALLOUT_COMPLEXITY_THRESHOLD = 18 ;
@@ -60,13 +51,6 @@ function deterministicChoice(seed, choices) {
6051 return choices [ index ] ;
6152}
6253
63- function hasThirdPartyProviderSecret ( level , seed , missingRemainder ) {
64- if ( level !== "beginner" ) {
65- return true ;
66- }
67- return seed % THIRD_PARTY_SECRET_BEGINNER_MODULO !== missingRemainder ;
68- }
69-
7054function resolveRepositoryOwnerType ( student , isEnterprise , seed ) {
7155 if ( isEnterprise ) {
7256 return "enterprise-organization" ;
@@ -153,6 +137,11 @@ function analyzeStepMarkdown(stepId, markdown, files = []) {
153137 text ,
154138 / \. l o c k \. y m l [ \s \S ] { 0 , 500 } \b ( c o m m i t (?: c h a n g e s ) ? | p u s h | a p p r o v e t h e c o m m i t ) \b | \b ( c o m m i t (?: c h a n g e s ) ? | p u s h | a p p r o v e t h e c o m m i t ) \b [ \s \S ] { 0 , 500 } \. l o c k \. y m l / gi
155139 ) ;
140+ const copilotRequestsWriteCueCount = countMatches (
141+ text ,
142+ / ^ \s * c o p i l o t - r e q u e s t s : \s * w r i t e \s * (?: # .* ) ? $ / gim
143+ ) ;
144+ const copilotGithubTokenCueCount = countMatches ( text , / \b C O P I L O T _ G I T H U B _ T O K E N \b / g) ;
156145 const complexity = clamp (
157146 0.12 +
158147 Math . min ( wordCount / WORD_COUNT_COMPLEXITY_THRESHOLD , 0.32 ) +
@@ -205,6 +194,8 @@ function analyzeStepMarkdown(stepId, markdown, files = []) {
205194 uiAlternativeCount,
206195 workflowCompileCueCount,
207196 workflowLockPublishCueCount,
197+ copilotRequestsWriteCueCount,
198+ copilotGithubTokenCueCount,
208199 authDemand,
209200 browserSupport,
210201 complexity,
@@ -368,26 +359,16 @@ function defaultEnvironmentForStudent(student, dayOfYear, runIndex = 0) {
368359 ( level === "advanced" ||
369360 level === "actions-user" ||
370361 ( isEnterprise ? seed % 6 !== 0 : seed % ( uiPreferred ? 4 : 5 ) !== 0 ) ) ;
371- const inferenceProvider = deterministicChoice ( seed + INFERENCE_PROVIDER_SEED_OFFSET , INFERENCE_PROVIDERS ) ;
372- const hasCopilotGithubToken =
373- isLoggedIn &&
374- ( inferenceProvider === "github"
375- ? seed % COPILOT_SECRET_PRIMARY_MODULO !== 0
376- : seed % COPILOT_SECRET_SECONDARY_MODULO === 0 ) ;
377- const hasAnthropicApiKey =
378- isLoggedIn &&
379- ( inferenceProvider === "anthropic"
380- ? hasThirdPartyProviderSecret ( level , seed , ANTHROPIC_MISSING_REMAINDER )
381- : seed % THIRD_PARTY_SECRET_SECONDARY_MODULO === 0 ) ;
382- const hasOpenAiApiKey =
383- isLoggedIn &&
384- ( inferenceProvider === "openai"
385- ? hasThirdPartyProviderSecret ( level , seed , OPENAI_MISSING_REMAINDER )
386- : seed % THIRD_PARTY_SECRET_SECONDARY_MODULO === 1 ) ;
387- const hasCopilotRequestsWrite =
388- inferenceProvider === "github"
389- ? seed % COPILOT_PERMISSION_GITHUB_MODULO !== 0
390- : seed % COPILOT_PERMISSION_OTHER_MODULO !== 0 ;
362+ const inferenceProvider = "github" ;
363+ // Model a mixed organization cohort: enterprise organizations have centralized
364+ // billing, while half of other organizations have enabled it.
365+ const centralizedCopilotBilling =
366+ repositoryOwnerType !== "personal" && ( isEnterprise || seed % 2 === 0 ) ;
367+ // The Step 7 transition fills these fields after the required model-access activity.
368+ const hasCopilotGithubToken = null ;
369+ const hasAnthropicApiKey = null ;
370+ const hasOpenAiApiKey = null ;
371+ const hasCopilotRequestsWrite = null ;
391372 const baseConfidence = clamp (
392373 0.3 +
393374 ( level === "advanced"
@@ -430,6 +411,7 @@ function defaultEnvironmentForStudent(student, dayOfYear, runIndex = 0) {
430411 } ,
431412 actions : {
432413 inferenceProvider,
414+ centralizedCopilotBilling,
433415 permissions : {
434416 copilotRequestsWrite : hasCopilotRequestsWrite
435417 } ,
@@ -609,11 +591,26 @@ function aggregateDropoutRates(monteCarlo, totalStudents, runsCount) {
609591 return dropoutRateByStep ;
610592}
611593
594+ function aggregateFailureCategories ( monteCarlo ) {
595+ const failureCategoriesByStep = { } ;
596+ for ( const result of monteCarlo ) {
597+ for ( const [ stepId , categories ] of Object . entries ( result . failureCategoriesByStep || { } ) ) {
598+ failureCategoriesByStep [ stepId ] ||= { } ;
599+ for ( const [ category , count ] of Object . entries ( categories ) ) {
600+ failureCategoriesByStep [ stepId ] [ category ] =
601+ ( failureCategoriesByStep [ stepId ] [ category ] || 0 ) + count ;
602+ }
603+ }
604+ }
605+ return failureCategoriesByStep ;
606+ }
607+
612608function simulateStudentsMonteCarlo ( students , date , runsCount = 100 , config = { } ) {
613609 const dayOfYear = toDayOfYear ( date ) ;
614610 return students . map ( ( student ) => {
615611 let successes = 0 ;
616612 const failuresByStep = { } ;
613+ const failureCategoriesByStep = { } ;
617614
618615 for ( let runIndex = 0 ; runIndex < runsCount ; runIndex ++ ) {
619616 const initialState =
@@ -638,6 +635,10 @@ function simulateStudentsMonteCarlo(students, date, runsCount = 100, config = {}
638635 } else if ( result . firstFailingStep ) {
639636 failuresByStep [ result . firstFailingStep ] =
640637 ( failuresByStep [ result . firstFailingStep ] || 0 ) + 1 ;
638+ const category = result . category || "unknown" ;
639+ failureCategoriesByStep [ result . firstFailingStep ] ||= { } ;
640+ failureCategoriesByStep [ result . firstFailingStep ] [ category ] =
641+ ( failureCategoriesByStep [ result . firstFailingStep ] [ category ] || 0 ) + 1 ;
641642 }
642643 }
643644
@@ -649,6 +650,7 @@ function simulateStudentsMonteCarlo(students, date, runsCount = 100, config = {}
649650 successes,
650651 successRate : successes / runsCount ,
651652 failuresByStep,
653+ failureCategoriesByStep,
652654 mostCommonFailureStep : sortedFailures . length > 0 ? sortedFailures [ 0 ] [ 0 ] : null
653655 } ;
654656 } ) ;
@@ -724,7 +726,8 @@ function runCli() {
724726 aggregate : {
725727 overallSuccessRate :
726728 monteCarlo . reduce ( ( sum , r ) => sum + r . successRate , 0 ) / monteCarlo . length ,
727- dropoutRateByStep : aggregateDropoutRates ( monteCarlo , students . length , runsCount )
729+ dropoutRateByStep : aggregateDropoutRates ( monteCarlo , students . length , runsCount ) ,
730+ failureCategoriesByStep : aggregateFailureCategories ( monteCarlo )
728731 }
729732 } ;
730733 } else {
@@ -761,7 +764,8 @@ const exportedApi = {
761764 replayWorkshop,
762765 simulateStudents,
763766 simulateStudentsMonteCarlo,
764- aggregateDropoutRates
767+ aggregateDropoutRates,
768+ aggregateFailureCategories
765769} ;
766770
767771module . exports = exportedApi ;
0 commit comments