4runr-os 2.0.27 → 2.0.31

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.
@@ -2020,7 +2020,7 @@ async function handleDebug(ctx, args) {
2020
2020
  ts: Date.now(),
2021
2021
  tag: 'ERR',
2022
2022
  level: 'ERROR',
2023
- msg: 'Usage: debug <ui|geo|resize|resize-test|resize-trace|dump-layout|listeners|stdout>',
2023
+ msg: 'Usage: debug <ui|geo|resize|resize-test|resize-torture|resize-trace|dump-layout|listeners|stdout>',
2024
2024
  }],
2025
2025
  };
2026
2026
  }
@@ -2363,6 +2363,272 @@ async function handleDebug(ctx, args) {
2363
2363
  ];
2364
2364
  return { events };
2365
2365
  }
2366
+ else if (subcommand === 'resize-torture' || subcommand === 'torture') {
2367
+ // Resize torture test - comprehensive widget duplication detection
2368
+ const { getUIRuntime } = await import('../ui/uiRuntime.js');
2369
+ const runtime = getUIRuntime();
2370
+ const screen = runtime.screen;
2371
+ const widgets = runtime.widgets;
2372
+ // Get current dimensions
2373
+ const { getTerminalGeometry } = await import('../tui/geometry.js');
2374
+ const geo = getTerminalGeometry(screen);
2375
+ // Count all widgets recursively
2376
+ function countWidgets(node, depth = 0) {
2377
+ const result = { total: 0, byType: {}, byId: {} };
2378
+ if (!node)
2379
+ return result;
2380
+ result.total = 1;
2381
+ // Track by type
2382
+ const type = node.type || 'unknown';
2383
+ result.byType[type] = (result.byType[type] || 0) + 1;
2384
+ // Track by ID (if available)
2385
+ const id = node.options?.id || node._id || 'no-id';
2386
+ result.byId[id] = (result.byId[id] || 0) + 1;
2387
+ // Recursively count children
2388
+ if (node.children && Array.isArray(node.children)) {
2389
+ for (const child of node.children) {
2390
+ const childResult = countWidgets(child, depth + 1);
2391
+ result.total += childResult.total;
2392
+ // Merge byType
2393
+ for (const [t, count] of Object.entries(childResult.byType)) {
2394
+ result.byType[t] = (result.byType[t] || 0) + count;
2395
+ }
2396
+ // Merge byId
2397
+ for (const [i, count] of Object.entries(childResult.byId)) {
2398
+ result.byId[i] = (result.byId[i] || 0) + count;
2399
+ }
2400
+ }
2401
+ }
2402
+ return result;
2403
+ }
2404
+ const widgetStats = countWidgets(screen);
2405
+ // Check for duplicates
2406
+ const duplicateTypes = [];
2407
+ for (const [type, count] of Object.entries(widgetStats.byType)) {
2408
+ if (count > 1 && type !== 'box' && type !== 'text' && type !== 'line') {
2409
+ duplicateTypes.push(`${type}=${count}`);
2410
+ }
2411
+ }
2412
+ const duplicateIds = [];
2413
+ for (const [id, count] of Object.entries(widgetStats.byId)) {
2414
+ if (count > 1 && id !== 'no-id') {
2415
+ duplicateIds.push(`${id}=${count}`);
2416
+ }
2417
+ }
2418
+ // Count specific widgets
2419
+ const postureCount = widgetStats.byType['box'] || 0; // Approximate
2420
+ const commandLineCount = screen.children.filter((w) => w === widgets.commandLine).length;
2421
+ // Get resize stats
2422
+ const resizeStats = debugUtils.getResizeStats();
2423
+ const events = [
2424
+ cmdEvent,
2425
+ {
2426
+ id: `dbg-torture-1-${Date.now()}`,
2427
+ ts: Date.now(),
2428
+ tag: 'DBG',
2429
+ level: 'INFO',
2430
+ msg: `=== RESIZE TORTURE TEST ===`,
2431
+ },
2432
+ {
2433
+ id: `dbg-torture-2-${Date.now()}`,
2434
+ ts: Date.now(),
2435
+ tag: 'DBG',
2436
+ level: 'INFO',
2437
+ msg: `Instructions: Toggle fullscreen ↔ windowed 10 times, then run this again.`,
2438
+ },
2439
+ {
2440
+ id: `dbg-torture-3-${Date.now()}`,
2441
+ ts: Date.now(),
2442
+ tag: 'DBG',
2443
+ level: 'INFO',
2444
+ msg: ``,
2445
+ },
2446
+ {
2447
+ id: `dbg-torture-4-${Date.now()}`,
2448
+ ts: Date.now(),
2449
+ tag: 'DBG',
2450
+ level: 'INFO',
2451
+ msg: `Current terminal: ${geo.cols}x${geo.rows} (source: ${geo.source})`,
2452
+ },
2453
+ {
2454
+ id: `dbg-torture-5-${Date.now()}`,
2455
+ ts: Date.now(),
2456
+ tag: 'DBG',
2457
+ level: 'INFO',
2458
+ msg: ``,
2459
+ },
2460
+ {
2461
+ id: `dbg-torture-6-${Date.now()}`,
2462
+ ts: Date.now(),
2463
+ tag: 'DBG',
2464
+ level: 'INFO',
2465
+ msg: `--- Widget Counts ---`,
2466
+ },
2467
+ {
2468
+ id: `dbg-torture-7-${Date.now()}`,
2469
+ ts: Date.now(),
2470
+ tag: 'DBG',
2471
+ level: 'INFO',
2472
+ msg: `Total widgets: ${widgetStats.total} (should be stable ~8-15)`,
2473
+ },
2474
+ {
2475
+ id: `dbg-torture-8-${Date.now()}`,
2476
+ ts: Date.now(),
2477
+ tag: 'DBG',
2478
+ level: 'INFO',
2479
+ msg: `Screen.children: ${screen.children.length}`,
2480
+ },
2481
+ {
2482
+ id: `dbg-torture-9-${Date.now()}`,
2483
+ ts: Date.now(),
2484
+ tag: 'DBG',
2485
+ level: 'INFO',
2486
+ msg: `Command line instances: ${commandLineCount} (must be 1)`,
2487
+ },
2488
+ {
2489
+ id: `dbg-torture-10-${Date.now()}`,
2490
+ ts: Date.now(),
2491
+ tag: 'DBG',
2492
+ level: 'INFO',
2493
+ msg: ``,
2494
+ },
2495
+ {
2496
+ id: `dbg-torture-11-${Date.now()}`,
2497
+ ts: Date.now(),
2498
+ tag: 'DBG',
2499
+ level: 'INFO',
2500
+ msg: `--- Widget Types ---`,
2501
+ },
2502
+ ];
2503
+ // Add widget type counts
2504
+ for (const [type, count] of Object.entries(widgetStats.byType)) {
2505
+ events.push({
2506
+ id: `dbg-torture-type-${type}-${Date.now()}`,
2507
+ ts: Date.now(),
2508
+ tag: 'DBG',
2509
+ level: 'INFO',
2510
+ msg: ` ${type}: ${count}`,
2511
+ });
2512
+ }
2513
+ events.push({
2514
+ id: `dbg-torture-12-${Date.now()}`,
2515
+ ts: Date.now(),
2516
+ tag: 'DBG',
2517
+ level: 'INFO',
2518
+ msg: ``,
2519
+ });
2520
+ events.push({
2521
+ id: `dbg-torture-13-${Date.now()}`,
2522
+ ts: Date.now(),
2523
+ tag: 'DBG',
2524
+ level: 'INFO',
2525
+ msg: `--- Resize Stats ---`,
2526
+ });
2527
+ events.push({
2528
+ id: `dbg-torture-14-${Date.now()}`,
2529
+ ts: Date.now(),
2530
+ tag: 'DBG',
2531
+ level: 'INFO',
2532
+ msg: `Resize events: ${resizeStats.eventCount}`,
2533
+ });
2534
+ events.push({
2535
+ id: `dbg-torture-15-${Date.now()}`,
2536
+ ts: Date.now(),
2537
+ tag: 'DBG',
2538
+ level: 'INFO',
2539
+ msg: `Resize applies: ${resizeStats.applyCount}`,
2540
+ });
2541
+ events.push({
2542
+ id: `dbg-torture-16-${Date.now()}`,
2543
+ ts: Date.now(),
2544
+ tag: 'DBG',
2545
+ level: 'INFO',
2546
+ msg: `UI rebuilds: ${resizeStats.uiRebuildCount || 0} (must be 0)`,
2547
+ });
2548
+ events.push({
2549
+ id: `dbg-torture-17-${Date.now()}`,
2550
+ ts: Date.now(),
2551
+ tag: 'DBG',
2552
+ level: 'INFO',
2553
+ msg: ``,
2554
+ });
2555
+ events.push({
2556
+ id: `dbg-torture-18-${Date.now()}`,
2557
+ ts: Date.now(),
2558
+ tag: 'DBG',
2559
+ level: duplicateTypes.length > 0 ? 'ERROR' : 'INFO',
2560
+ msg: `--- Duplication Check ---`,
2561
+ });
2562
+ if (duplicateTypes.length > 0) {
2563
+ events.push({
2564
+ id: `dbg-torture-19-${Date.now()}`,
2565
+ ts: Date.now(),
2566
+ tag: 'DBG',
2567
+ level: 'ERROR',
2568
+ msg: `⚠ FAIL: Duplicate widget types detected: ${duplicateTypes.join(', ')}`,
2569
+ });
2570
+ }
2571
+ else {
2572
+ events.push({
2573
+ id: `dbg-torture-19-${Date.now()}`,
2574
+ ts: Date.now(),
2575
+ tag: 'DBG',
2576
+ level: 'INFO',
2577
+ msg: `✓ PASS: No duplicate widget types`,
2578
+ });
2579
+ }
2580
+ if (commandLineCount > 1) {
2581
+ events.push({
2582
+ id: `dbg-torture-20-${Date.now()}`,
2583
+ ts: Date.now(),
2584
+ tag: 'DBG',
2585
+ level: 'ERROR',
2586
+ msg: `⚠ FAIL: Multiple command lines (${commandLineCount})`,
2587
+ });
2588
+ }
2589
+ else {
2590
+ events.push({
2591
+ id: `dbg-torture-20-${Date.now()}`,
2592
+ ts: Date.now(),
2593
+ tag: 'DBG',
2594
+ level: 'INFO',
2595
+ msg: `✓ PASS: Single command line`,
2596
+ });
2597
+ }
2598
+ if ((resizeStats.uiRebuildCount || 0) > 0) {
2599
+ events.push({
2600
+ id: `dbg-torture-21-${Date.now()}`,
2601
+ ts: Date.now(),
2602
+ tag: 'DBG',
2603
+ level: 'ERROR',
2604
+ msg: `⚠ FAIL: UI was rebuilt ${resizeStats.uiRebuildCount} times (should be 0)`,
2605
+ });
2606
+ }
2607
+ else {
2608
+ events.push({
2609
+ id: `dbg-torture-21-${Date.now()}`,
2610
+ ts: Date.now(),
2611
+ tag: 'DBG',
2612
+ level: 'INFO',
2613
+ msg: `✓ PASS: No UI rebuilds`,
2614
+ });
2615
+ }
2616
+ events.push({
2617
+ id: `dbg-torture-22-${Date.now()}`,
2618
+ ts: Date.now(),
2619
+ tag: 'DBG',
2620
+ level: 'INFO',
2621
+ msg: ``,
2622
+ });
2623
+ events.push({
2624
+ id: `dbg-torture-23-${Date.now()}`,
2625
+ ts: Date.now(),
2626
+ tag: 'DBG',
2627
+ level: 'INFO',
2628
+ msg: `=== END TORTURE TEST ===`,
2629
+ });
2630
+ return { events };
2631
+ }
2366
2632
  else if (subcommand === 'resize-trace' || subcommand === 'trace-resize') {
2367
2633
  // Resize trace mode toggle
2368
2634
  const { setResizeTraceEnabled, getResizeTraceEnabled } = await import('../ui/phase1RuntimeClean.js');
@@ -2477,7 +2743,11 @@ async function handleDebug(ctx, args) {
2477
2743
  const stdoutRows = process.stdout.rows ?? 'N/A';
2478
2744
  const stderrCols = process.stderr.columns ?? 'N/A';
2479
2745
  const stderrRows = process.stderr.rows ?? 'N/A';
2480
- // Compute layout to get metadata
2746
+ // Get actual geometry being used (authoritative source)
2747
+ const { getTerminalGeometry, getTerminalSize } = await import('../tui/geometry.js');
2748
+ const actualSize = getTerminalSize(screen);
2749
+ const actualGeo = getTerminalGeometry(screen);
2750
+ // Compute layout to get metadata (pass fresh screen to use current geometry)
2481
2751
  const layoutResult = computePhase1Layout(screen.width ?? 80, screen.height ?? 24, screen);
2482
2752
  const events = [
2483
2753
  cmdEvent,
@@ -2551,60 +2821,122 @@ async function handleDebug(ctx, args) {
2551
2821
  level: 'INFO',
2552
2822
  msg: ` process.stderr.rows: ${stderrRows}`,
2553
2823
  },
2824
+ {
2825
+ id: `dbg-fullscreen-11-${Date.now()}`,
2826
+ ts: Date.now(),
2827
+ tag: 'DBG',
2828
+ level: 'INFO',
2829
+ msg: ``,
2830
+ },
2831
+ {
2832
+ id: `dbg-fullscreen-12-${Date.now()}`,
2833
+ ts: Date.now(),
2834
+ tag: 'DBG',
2835
+ level: 'INFO',
2836
+ msg: `C. GEOMETRY USED (authoritative):`,
2837
+ },
2838
+ {
2839
+ id: `dbg-fullscreen-13-${Date.now()}`,
2840
+ ts: Date.now(),
2841
+ tag: 'DBG',
2842
+ level: 'INFO',
2843
+ msg: ` getTerminalSize() source: ${actualSize.source}`,
2844
+ },
2845
+ {
2846
+ id: `dbg-fullscreen-14-${Date.now()}`,
2847
+ ts: Date.now(),
2848
+ tag: 'DBG',
2849
+ level: 'INFO',
2850
+ msg: ` getTerminalSize() cols: ${actualSize.cols}`,
2851
+ },
2852
+ {
2853
+ id: `dbg-fullscreen-15-${Date.now()}`,
2854
+ ts: Date.now(),
2855
+ tag: 'DBG',
2856
+ level: 'INFO',
2857
+ msg: ` getTerminalSize() rows: ${actualSize.rows}`,
2858
+ },
2859
+ {
2860
+ id: `dbg-fullscreen-16-${Date.now()}`,
2861
+ ts: Date.now(),
2862
+ tag: 'DBG',
2863
+ level: 'INFO',
2864
+ msg: ` getTerminalGeometry() safeCols: ${actualGeo.safeCols}`,
2865
+ },
2866
+ {
2867
+ id: `dbg-fullscreen-17-${Date.now()}`,
2868
+ ts: Date.now(),
2869
+ tag: 'DBG',
2870
+ level: 'INFO',
2871
+ msg: ` getTerminalGeometry() safeRows: ${actualGeo.safeRows}`,
2872
+ },
2873
+ {
2874
+ id: `dbg-fullscreen-18-${Date.now()}`,
2875
+ ts: Date.now(),
2876
+ tag: 'DBG',
2877
+ level: 'INFO',
2878
+ msg: ` Terminal mode: ${actualGeo.mode}`,
2879
+ },
2554
2880
  ];
2555
2881
  // Add layout metadata if available
2556
2882
  if (layoutResult.ok && layoutResult.metadata) {
2557
2883
  const m = layoutResult.metadata;
2558
2884
  events.push({
2559
- id: `dbg-fullscreen-11-${Date.now()}`,
2885
+ id: `dbg-fullscreen-19-${Date.now()}`,
2886
+ ts: Date.now(),
2887
+ tag: 'DBG',
2888
+ level: 'INFO',
2889
+ msg: ``,
2890
+ }, {
2891
+ id: `dbg-fullscreen-20-${Date.now()}`,
2560
2892
  ts: Date.now(),
2561
2893
  tag: 'DBG',
2562
2894
  level: 'INFO',
2563
2895
  msg: `B. LAYOUT METADATA (what layout is using):`,
2564
2896
  }, {
2565
- id: `dbg-fullscreen-12-${Date.now()}`,
2897
+ id: `dbg-fullscreen-21-${Date.now()}`,
2566
2898
  ts: Date.now(),
2567
2899
  tag: 'DBG',
2568
2900
  level: 'INFO',
2569
- msg: ` layout.colsUsed: ${m.colsUsed}`,
2901
+ msg: ` layout.colsUsed: ${m.colsUsed} (should match safeCols or be close)`,
2570
2902
  }, {
2571
- id: `dbg-fullscreen-13-${Date.now()}`,
2903
+ id: `dbg-fullscreen-22-${Date.now()}`,
2572
2904
  ts: Date.now(),
2573
2905
  tag: 'DBG',
2574
2906
  level: 'INFO',
2575
- msg: ` layout.rowsUsed: ${m.rowsUsed}`,
2907
+ msg: ` layout.rowsUsed: ${m.rowsUsed} (should match safeRows or be close)`,
2576
2908
  }, {
2577
- id: `dbg-fullscreen-14-${Date.now()}`,
2909
+ id: `dbg-fullscreen-23-${Date.now()}`,
2578
2910
  ts: Date.now(),
2579
2911
  tag: 'DBG',
2580
2912
  level: 'INFO',
2581
2913
  msg: ` layout.availableWidth: ${m.availableWidth}`,
2582
2914
  }, {
2583
- id: `dbg-fullscreen-15-${Date.now()}`,
2915
+ id: `dbg-fullscreen-24-${Date.now()}`,
2584
2916
  ts: Date.now(),
2585
2917
  tag: 'DBG',
2586
2918
  level: 'INFO',
2587
2919
  msg: ` layout.availableHeight: ${m.availableHeight}`,
2588
2920
  }, {
2589
- id: `dbg-fullscreen-16-${Date.now()}`,
2921
+ id: `dbg-fullscreen-25-${Date.now()}`,
2590
2922
  ts: Date.now(),
2591
2923
  tag: 'DBG',
2592
2924
  level: 'INFO',
2593
2925
  msg: ` layout.gutters: ${m.gutters}`,
2594
2926
  }, {
2595
- id: `dbg-fullscreen-17-${Date.now()}`,
2927
+ id: `dbg-fullscreen-26-${Date.now()}`,
2596
2928
  ts: Date.now(),
2597
2929
  tag: 'DBG',
2598
2930
  level: 'INFO',
2599
- msg: ` layout.margins: ${m.margins}`,
2931
+ msg: ` layout.margins: ${m.margins} (browser=${actualGeo.mode === 'browser' ? 'yes' : 'no'})`,
2600
2932
  }, {
2601
- id: `dbg-fullscreen-18-${Date.now()}`,
2933
+ id: `dbg-fullscreen-27-${Date.now()}`,
2602
2934
  ts: Date.now(),
2603
2935
  tag: 'DBG',
2604
2936
  level: 'INFO',
2605
2937
  msg: ` layout.minCols: ${m.minCols}`,
2606
2938
  }, {
2607
- id: `dbg-fullscreen-19-${Date.now()}`,
2939
+ id: `dbg-fullscreen-28-${Date.now()}`,
2608
2940
  ts: Date.now(),
2609
2941
  tag: 'DBG',
2610
2942
  level: 'INFO',
@@ -2613,13 +2945,39 @@ async function handleDebug(ctx, args) {
2613
2945
  }
2614
2946
  else {
2615
2947
  events.push({
2616
- id: `dbg-fullscreen-11-${Date.now()}`,
2948
+ id: `dbg-fullscreen-19-${Date.now()}`,
2949
+ ts: Date.now(),
2950
+ tag: 'ERR',
2951
+ level: 'ERROR',
2952
+ msg: ``,
2953
+ }, {
2954
+ id: `dbg-fullscreen-20-${Date.now()}`,
2617
2955
  ts: Date.now(),
2618
2956
  tag: 'ERR',
2619
2957
  level: 'ERROR',
2620
2958
  msg: `B. LAYOUT ERROR: ${layoutResult.errorMessage || 'Layout computation failed'}`,
2621
2959
  });
2622
2960
  }
2961
+ // Add diagnostic summary
2962
+ events.push({
2963
+ id: `dbg-fullscreen-summary-${Date.now()}`,
2964
+ ts: Date.now(),
2965
+ tag: 'DBG',
2966
+ level: 'INFO',
2967
+ msg: ``,
2968
+ }, {
2969
+ id: `dbg-fullscreen-summary2-${Date.now()}`,
2970
+ ts: Date.now(),
2971
+ tag: 'DBG',
2972
+ level: 'INFO',
2973
+ msg: `DIAGNOSTIC: If cols/rows DON'T change in fullscreen → Windows Terminal settings (font/padding).`,
2974
+ }, {
2975
+ id: `dbg-fullscreen-summary3-${Date.now()}`,
2976
+ ts: Date.now(),
2977
+ tag: 'DBG',
2978
+ level: 'INFO',
2979
+ msg: `If cols/rows DO change but colsUsed stays small → layout clamping bug (check code).`,
2980
+ });
2623
2981
  events.push({
2624
2982
  id: `dbg-fullscreen-end-${Date.now()}`,
2625
2983
  ts: Date.now(),
@@ -2662,7 +3020,7 @@ async function handleDebug(ctx, args) {
2662
3020
  ts: Date.now(),
2663
3021
  tag: 'ERR',
2664
3022
  level: 'ERROR',
2665
- msg: `Unknown debug subcommand: ${subcommand} (try: ui, geo, resize, resize-test, resize-trace, dump-layout, fullscreen, listeners, stdout)`,
3023
+ msg: `Unknown debug subcommand: ${subcommand} (try: ui, geo, resize, resize-test, resize-torture, resize-trace, dump-layout, fullscreen, listeners, stdout)`,
2666
3024
  }],
2667
3025
  };
2668
3026
  }