@@ -137,7 +137,7 @@ function parseArgs(argv) {
137137 return args ;
138138}
139139
140- function runNodeScript ( scriptPath , scriptArgs , options = { } ) {
140+ function runNodeScript ( scriptPath , scriptArgs ) {
141141 const result = spawnSync ( process . execPath , [ scriptPath , ...scriptArgs ] , {
142142 cwd : process . cwd ( ) ,
143143 encoding : 'utf8'
@@ -155,7 +155,7 @@ function runNodeScript(scriptPath, scriptArgs, options = {}) {
155155 status : result . status ,
156156 error : result . error ? result . error . message : '' ,
157157 command : `${ path . basename ( scriptPath ) } ${ scriptArgs . join ( ' ' ) } ` . trim ( ) ,
158- note : options . note || ''
158+ pendingTodo : false
159159 } ;
160160}
161161
@@ -181,70 +181,85 @@ function hasRealCohortData(rootDir) {
181181 if ( ! fs . existsSync ( rootDir ) ) {
182182 return false ;
183183 }
184- const cohortNames = [ 'small' , 'medium' , 'large' ] ;
185- for ( const cohort of cohortNames ) {
186- const cohortRoot = path . join ( rootDir , cohort ) ;
187- if ( ! fs . existsSync ( cohortRoot ) ) {
184+ for ( const cohort of [ 'small' , 'medium' , 'large' ] ) {
185+ if ( ! fs . existsSync ( path . join ( rootDir , cohort ) ) ) {
188186 return false ;
189187 }
190188 }
191189 return true ;
192190}
193191
194- function renderStatus ( ok , blocked = false ) {
195- if ( blocked ) {
196- return 'BLOCKED ' ;
192+ function renderStatus ( ok , pendingTodo = false ) {
193+ if ( pendingTodo ) {
194+ return 'TODO ' ;
197195 }
198196 return ok ? 'PASS' : 'FAIL' ;
199197}
200198
199+ function buildFutureTodoBacklog ( realCohortsRoot ) {
200+ return [
201+ `Collect real macOS cohorts under ${ path . join ( realCohortsRoot , 'small|medium|large' , 'macos' , 'baseline|pilot' ) } .` ,
202+ `Collect real Android cohorts under ${ path . join ( realCohortsRoot , 'small|medium|large' , 'android' , 'baseline|pilot' ) } .` ,
203+ `Collect real iOS cohorts under ${ path . join ( realCohortsRoot , 'small|medium|large' , 'ios' , 'baseline|pilot' ) } .` ,
204+ 'Ensure each platform has >=10 sessions for baseline and pilot in each cohort.' ,
205+ 'Run perf:startup:cohorts:verify on real datasets and archive report-cohorts-real.md as release evidence.'
206+ ] ;
207+ }
208+
201209function buildReport ( summary , args ) {
202210 const lines = [
203211 '# Startup Plan B Signoff Report' ,
204212 '' ,
205213 `Generated at: ${ summary . generatedAt } ` ,
206214 '' ,
207- '## 中文 ' ,
215+ '## Chinese ' ,
208216 '' ,
209- '### 分层签收结论 ' ,
210- `- 工程签收(无多端硬件) : ${ renderStatus ( summary . engineeringGatePass ) } ` ,
211- `- 发布签收(真实多端硬件) : ${ renderStatus ( summary . releaseGatePass , summary . releaseGateBlocked ) } ` ,
217+ '### Layered Signoff ' ,
218+ `- Engineering signoff (no multi-device hardware) : ${ renderStatus ( summary . engineeringGatePass ) } ` ,
219+ `- Release signoff (real multi-device hardware) : ${ renderStatus ( summary . releaseGatePass , summary . releaseGatePendingTodo ) } ` ,
212220 '' ,
213- '### 执行明细 ' ,
221+ '### Execution Detail ' ,
214222 '| Step | Status | Command |' ,
215223 '|---|---|---|'
216224 ] ;
217225
218226 for ( const step of summary . steps ) {
219- lines . push ( `| ${ step . name } | ${ renderStatus ( step . ok , step . blocked ) } | \`${ step . command } \` |` ) ;
227+ lines . push ( `| ${ step . name } | ${ renderStatus ( step . ok , step . pendingTodo ) } | \`${ step . command } \` |` ) ;
220228 }
221229
222230 lines . push ( '' ) ;
223- lines . push ( '### 说明' ) ;
224- lines . push ( `- Windows 根目录: \`${ args . windowsRoot } \`` ) ;
225- lines . push ( `- 模拟多端目录: \`${ args . simulatedRoot } \`` ) ;
226- lines . push ( `- 模拟三规模目录: \`${ args . simulatedCohortsRoot } \`` ) ;
227- lines . push ( `- 真实三规模目录: \`${ args . realCohortsRoot } \`` ) ;
228- lines . push ( `- 门禁阈值: TTI P50 >= ${ args . minTtiImprove } %, TFS P50 >= ${ args . minTfsImprove } %` ) ;
229- lines . push ( `- 样本门槛: 每平台 baseline/pilot >= ${ args . minSessionsPerPlatform } ` ) ;
230- lines . push ( '- 说明: 无多端硬件时,发布签收会标记为 BLOCKED,不会误判为 PASS。' ) ;
231+ lines . push ( '### Notes' ) ;
232+ lines . push ( `- Windows root: \`${ args . windowsRoot } \`` ) ;
233+ lines . push ( `- Simulated platform root: \`${ args . simulatedRoot } \`` ) ;
234+ lines . push ( `- Simulated cohorts root: \`${ args . simulatedCohortsRoot } \`` ) ;
235+ lines . push ( `- Real cohorts root: \`${ args . realCohortsRoot } \`` ) ;
236+ lines . push ( `- Gate thresholds: TTI P50 >= ${ args . minTtiImprove } %, TFS P50 >= ${ args . minTfsImprove } %` ) ;
237+ lines . push ( `- Session floor: baseline/pilot >= ${ args . minSessionsPerPlatform } ` ) ;
238+ lines . push ( '- Without real multi-device data, release signoff is TODO and tracked in backlog.' ) ;
239+ lines . push ( '' ) ;
240+ lines . push ( '### Future TODO Backlog' ) ;
241+ for ( const item of summary . futureTodos ) {
242+ lines . push ( `- [ ] ${ item } ` ) ;
243+ }
231244
232245 lines . push ( '' ) ;
233246 lines . push ( '## English' ) ;
234247 lines . push ( '' ) ;
235- lines . push ( '### Signoff Summary ' ) ;
236- lines . push ( `- Engineering gate (no multi-device hardware): ${ renderStatus ( summary . engineeringGatePass ) } ` ) ;
237- lines . push ( `- Release gate (real multi-device hardware): ${ renderStatus ( summary . releaseGatePass , summary . releaseGateBlocked ) } ` ) ;
248+ lines . push ( '### Layered Signoff ' ) ;
249+ lines . push ( `- Engineering signoff (no multi-device hardware): ${ renderStatus ( summary . engineeringGatePass ) } ` ) ;
250+ lines . push ( `- Release signoff (real multi-device hardware): ${ renderStatus ( summary . releaseGatePass , summary . releaseGatePendingTodo ) } ` ) ;
238251 lines . push ( '' ) ;
239- lines . push ( '### Details ' ) ;
252+ lines . push ( '### Execution Detail ' ) ;
240253 lines . push ( '| Step | Status | Command |' ) ;
241254 lines . push ( '|---|---|---|' ) ;
242255 for ( const step of summary . steps ) {
243- lines . push ( `| ${ step . name } | ${ renderStatus ( step . ok , step . blocked ) } | \`${ step . command } \` |` ) ;
256+ lines . push ( `| ${ step . name } | ${ renderStatus ( step . ok , step . pendingTodo ) } | \`${ step . command } \` |` ) ;
244257 }
245258 lines . push ( '' ) ;
246- lines . push ( '### Notes' ) ;
247- lines . push ( '- Release gate remains BLOCKED without real macOS/Android/iOS cohort datasets.' ) ;
259+ lines . push ( '### Future TODO Backlog' ) ;
260+ for ( const item of summary . futureTodos ) {
261+ lines . push ( `- [ ] ${ item } ` ) ;
262+ }
248263
249264 return lines . join ( '\n' ) ;
250265}
@@ -265,7 +280,6 @@ function main() {
265280
266281 const steps = [ ] ;
267282
268- // Step 1: Windows baseline/pilot compare gate
269283 const windowsCompareOut = path . join ( windowsRoot , 'report-phase1-windows-compare.md' ) ;
270284 const stepWindowsCompare = runNodeScript ( compareScript , [
271285 '--baseline' , path . join ( windowsRoot , 'baseline' ) ,
@@ -274,9 +288,8 @@ function main() {
274288 '--min-tti-improve' , String ( args . minTtiImprove ) ,
275289 '--min-tfs-improve' , String ( args . minTfsImprove )
276290 ] ) ;
277- steps . push ( { name : 'windows-compare' , ...stepWindowsCompare , blocked : false } ) ;
291+ steps . push ( { name : 'windows-compare' , ...stepWindowsCompare } ) ;
278292
279- // Step 2: Windows matrix strict gate
280293 const windowsMatrixOut = path . join ( windowsRoot , 'report-phase1-windows-matrix.md' ) ;
281294 const stepWindowsMatrix = runNodeScript ( matrixScript , [
282295 '--root' , windowsRoot ,
@@ -286,24 +299,22 @@ function main() {
286299 '--min-tfs-improve' , String ( args . minTfsImprove ) ,
287300 '--strict'
288301 ] ) ;
289- steps . push ( { name : 'windows-matrix' , ...stepWindowsMatrix , blocked : false } ) ;
302+ steps . push ( { name : 'windows-matrix' , ...stepWindowsMatrix } ) ;
290303
291- // Step 3: Simulated multi-platform matrix preparation
292304 const stepSimulate = runNodeScript ( simulateScript , [
293305 '--seed-root' , windowsRoot ,
294306 '--out-root' , simulatedRoot ,
295307 '--sessions-per-platform' , String ( args . sessionsPerPlatform ) ,
296308 '--seed' , String ( args . seed )
297309 ] ) ;
298- steps . push ( { name : 'simulate-platform-logs' , ...stepSimulate , blocked : false } ) ;
310+ steps . push ( { name : 'simulate-platform-logs' , ...stepSimulate } ) ;
299311
300- // Step 4: Simulated cohorts gate
301312 let stepSimulatedCohorts = {
302313 ok : false ,
303314 status : 1 ,
304315 error : '' ,
305316 command : `${ path . basename ( cohortsScript ) } --root ${ simulatedCohortsRoot } ` ,
306- blocked : false
317+ pendingTodo : false
307318 } ;
308319 if ( stepSimulate . ok ) {
309320 ensureDir ( simulatedCohortsRoot ) ;
@@ -324,15 +335,14 @@ function main() {
324335 } else {
325336 stepSimulatedCohorts . error = 'skip due to simulation generation failure' ;
326337 }
327- steps . push ( { name : 'simulated-cohorts-gate' , ...stepSimulatedCohorts , blocked : false } ) ;
338+ steps . push ( { name : 'simulated-cohorts-gate' , ...stepSimulatedCohorts } ) ;
328339
329- // Step 5: Real cohorts gate (blocked when no hardware datasets)
330340 let stepRealCohorts = {
331341 ok : false ,
332342 status : null ,
333343 error : '' ,
334344 command : `${ path . basename ( cohortsScript ) } --root ${ realCohortsRoot } ` ,
335- blocked : false
345+ pendingTodo : false
336346 } ;
337347 const realCohortsAvailable = hasRealCohortData ( realCohortsRoot ) ;
338348 if ( realCohortsAvailable ) {
@@ -351,21 +361,23 @@ function main() {
351361 status : null ,
352362 error : 'no real multi-device cohort dataset found' ,
353363 command : `${ path . basename ( cohortsScript ) } --root ${ realCohortsRoot } ` ,
354- blocked : true
364+ pendingTodo : true
355365 } ;
356366 }
357367 steps . push ( { name : 'real-cohorts-gate' , ...stepRealCohorts } ) ;
358368
359369 const engineeringGatePass = stepWindowsCompare . ok && stepWindowsMatrix . ok && stepSimulatedCohorts . ok ;
360- const releaseGateBlocked = stepRealCohorts . blocked === true ;
361- const releaseGatePass = ! releaseGateBlocked && stepRealCohorts . ok ;
370+ const releaseGatePendingTodo = stepRealCohorts . pendingTodo === true ;
371+ const releaseGatePass = ! releaseGatePendingTodo && stepRealCohorts . ok ;
372+ const futureTodos = releaseGatePendingTodo ? buildFutureTodoBacklog ( args . realCohortsRoot ) : [ ] ;
362373
363374 const summary = {
364375 generatedAt : new Date ( ) . toISOString ( ) ,
365376 steps,
366377 engineeringGatePass,
367378 releaseGatePass,
368- releaseGateBlocked
379+ releaseGatePendingTodo,
380+ futureTodos
369381 } ;
370382
371383 const report = buildReport ( summary , args ) ;
@@ -374,7 +386,7 @@ function main() {
374386 console . log ( `[startup-signoff] Report written: ${ outPath } ` ) ;
375387 console . log (
376388 `[startup-signoff] engineeringGate=${ renderStatus ( engineeringGatePass ) } , ` +
377- `releaseGate=${ renderStatus ( releaseGatePass , releaseGateBlocked ) } `
389+ `releaseGate=${ renderStatus ( releaseGatePass , releaseGatePendingTodo ) } `
378390 ) ;
379391
380392 if ( args . strictEngineering && ! engineeringGatePass ) {
0 commit comments