@rightkit/release 0.6.16 → 0.6.20

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 (64) hide show
  1. package/README.md +7 -3
  2. package/admission-host.mjs +9 -5
  3. package/app-closure.mjs +9 -3
  4. package/bin/darwin/rightkit-swap +0 -0
  5. package/bin/rightkit-gate.mjs +469 -102
  6. package/bin/rightkit-linux-setup.mjs +15 -0
  7. package/broker-admission.mjs +184 -0
  8. package/broker-wire.mjs +15 -3
  9. package/build-launch.mjs +4 -1
  10. package/build-ownership.mjs +125 -36
  11. package/build-release.mjs +39 -7
  12. package/cargo-contract.mjs +4 -1
  13. package/cargo-guard.mjs +25 -5
  14. package/cargo-target-shim.mjs +56 -7
  15. package/clippy-route.mjs +8 -7
  16. package/crate-graph.mjs +5 -1
  17. package/credential-provider.mjs +174 -0
  18. package/deterministic-scans.mjs +247 -40
  19. package/development.mjs +8 -0
  20. package/disk-policy.mjs +2 -2
  21. package/gate-executor.mjs +5 -3
  22. package/gate-stages.mjs +34 -3
  23. package/gradle-lock.mjs +206 -0
  24. package/heavy-command.mjs +33 -2
  25. package/linux-artifacts.mjs +2 -1
  26. package/linux-setup.mjs +106 -0
  27. package/local-publish.mjs +21 -10
  28. package/local-qualify.mjs +9 -3
  29. package/managed-cargo.mjs +54 -18
  30. package/ownership-lockfiles.mjs +14 -3
  31. package/ownership.mjs +5 -1
  32. package/package.json +17 -11
  33. package/protected-credentials.mjs +13 -5
  34. package/publication-gate.mjs +9 -4
  35. package/python-lock.mjs +209 -0
  36. package/qa-admission-policy.json +32 -0
  37. package/qa-layout.mjs +229 -0
  38. package/qa-runner.mjs +156 -0
  39. package/release-artifact-path.mjs +29 -3
  40. package/release-config.mjs +1 -1
  41. package/release-qa-command.mjs +58 -8
  42. package/release-qa-policy.mjs +5 -3
  43. package/release-source-identity.mjs +1 -0
  44. package/release.mjs +10 -2
  45. package/rightkit-ownership.json +1465 -79
  46. package/rightkit-registry-resolver.mjs +7 -3
  47. package/rightkit-versions.json +173 -36
  48. package/runtime-budget-classes.json +4 -0
  49. package/runtime-budgets.mjs +5 -1
  50. package/shell-contract.mjs +1 -1
  51. package/snapshot-broker.mjs +107 -9
  52. package/snapshot-check.mjs +2 -0
  53. package/snapshot-request.mjs +20 -3
  54. package/snapshot.mjs +11 -4
  55. package/stage-commands.mjs +2 -1
  56. package/stage-ledger.mjs +15 -0
  57. package/stage-sidecar.mjs +17 -3
  58. package/suite-signing.mjs +40 -4
  59. package/swift-lock.mjs +334 -0
  60. package/test-command.mjs +13 -3
  61. package/tool-build.mjs +156 -0
  62. package/unit-gate.mjs +9 -2
  63. package/windows-identity-plan.mjs +119 -0
  64. package/windows-identity.mjs +136 -0
@@ -25,10 +25,14 @@
25
25
  import { createHash } from "node:crypto";
26
26
  import { existsSync, mkdirSync, readFileSync, readdirSync, renameSync, writeFileSync } from "node:fs";
27
27
  import path from "node:path";
28
- import { parse as parseToml } from "smol-toml";
28
+ import { createRequire } from "node:module";
29
+ // smol-toml loads on first parse, not at import: CI jobs whose closure reaches this module without installing
30
+ // @rightkit/release dependencies (publish.yml crates pack, bind) never parse TOML here (rightkit#1341).
31
+ let smolTomlParse;
32
+ const parseToml = (text) => (smolTomlParse ??= createRequire(import.meta.url)("smol-toml").parse)(text);
29
33
  import { isExcludedScanDirectory } from "./cargo-contract.mjs";
30
34
  import { resolveSharedCacheRoot } from "./cache-policy.mjs";
31
- import { parsePnpmYaml } from "./ownership-lockfiles.mjs";
35
+ import { parsePnpmYaml, pnpmProjectDocument } from "./ownership-lockfiles.mjs";
32
36
  import { UPDATE_COMMAND } from "./outdated-command.mjs";
33
37
  import { compareVersions } from "./rightkit-sync.mjs";
34
38
 
@@ -132,7 +136,7 @@ function cargoLockEntries(file) {
132
136
 
133
137
  function pnpmLockEntries(file) {
134
138
  let lock;
135
- try { lock = parsePnpmYaml(readFileSync(file, "utf8")); } catch (error) { throw new RightKitRegistryError("bad-lock", `${file} is not a readable pnpm-lock.yaml: ${error.message}`); }
139
+ try { lock = parsePnpmYaml(pnpmProjectDocument(readFileSync(file, "utf8"))); } catch (error) { throw new RightKitRegistryError("bad-lock", `${file} is not a readable pnpm-lock.yaml: ${error.message}`); }
136
140
  const entries = [];
137
141
  for (const [key, meta] of Object.entries(lock.packages ?? {})) {
138
142
  const match = /^(@[^/]+\/[^@]+|[^@]+)@(.+)$/.exec(String(key).replace(/^\//, ""));
@@ -13,17 +13,34 @@
13
13
  "@rightkit/updates": "0.2.3"
14
14
  },
15
15
  "stagedNpm": {
16
+ "@rightkit/agent-ui-approvals": "0.1.0",
17
+ "@rightkit/agent-ui-components": "0.1.0",
18
+ "@rightkit/agent-ui-core": "0.1.0",
19
+ "@rightkit/agent-ui-hooks": "0.1.0",
20
+ "@rightkit/agent-ui-settings": "0.1.0",
21
+ "@rightkit/agent-ui-web": "0.1.0",
22
+ "@rightkit/agent-ui-workbench": "0.1.0",
16
23
  "@rightkit/app-update": "0.4.2",
17
24
  "@rightkit/ax": "0.3.2",
18
25
  "@rightkit/ci": "0.3.2",
26
+ "@rightkit/config": "0.1.0",
27
+ "@rightkit/dictation-ui": "0.1.0",
19
28
  "@rightkit/doc-render": "0.2.1",
29
+ "@rightkit/document-core": "0.1.0",
30
+ "@rightkit/document-workspace": "0.1.0",
31
+ "@rightkit/docx-editor": "0.1.0",
20
32
  "@rightkit/hooks": "0.1.2",
33
+ "@rightkit/html-editor": "0.1.0",
21
34
  "@rightkit/i18n": "0.2.0",
22
35
  "@rightkit/issues-tracker": "0.1.0",
23
36
  "@rightkit/legal": "0.6.3",
24
37
  "@rightkit/legal-ui": "0.4.1",
38
+ "@rightkit/library-ui": "0.1.0",
25
39
  "@rightkit/license": "0.3.0",
26
40
  "@rightkit/logs": "0.1.6",
41
+ "@rightkit/mail-ui": "0.1.0",
42
+ "@rightkit/markdown-editor": "0.1.0",
43
+ "@rightkit/media-gen-ui": "0.1.0",
27
44
  "@rightkit/media-ui": "0.3.4",
28
45
  "@rightkit/onboarding": "0.6.3",
29
46
  "@rightkit/outdated": "0.3.0",
@@ -32,11 +49,17 @@
32
49
  "@rightkit/qa": "0.6.2",
33
50
  "@rightkit/qa-chrome": "0.1.12",
34
51
  "@rightkit/react-shell": "0.7.2",
35
- "@rightkit/release": "0.6.16",
52
+ "@rightkit/release": "0.6.20",
36
53
  "@rightkit/settings-ui": "0.7.0",
54
+ "@rightkit/social-ui": "0.1.0",
55
+ "@rightkit/system-ui": "0.1.0",
37
56
  "@rightkit/tauri": "0.3.0",
38
57
  "@rightkit/tauri-shell": "0.1.3",
39
- "@rightkit/theme": "0.3.1"
58
+ "@rightkit/terminal-ui": "0.1.0",
59
+ "@rightkit/theme": "0.3.1",
60
+ "@rightkit/video-ui": "0.1.0",
61
+ "@rightkit/waitlist-worker": "0.1.0",
62
+ "@rightkit/workspace-ui": "0.1.0"
40
63
  },
41
64
  "legacyNpm": {
42
65
  "@rightkit/app-shell": [
@@ -455,73 +478,176 @@
455
478
  "rightkit-tauri": "0.1.0"
456
479
  },
457
480
  "stagedCargo": {
458
- "rightkit-agent-tools": "0.3.0",
459
- "rightkit-agents": "0.2.0",
481
+ "rightkit-adapt": "0.1.0",
482
+ "rightkit-adapt-taste": "0.1.0",
483
+ "rightkit-agent-demo": "0.1.0",
484
+ "rightkit-agent-engine-api-integrations": "0.1.0",
485
+ "rightkit-agent-engine-api-platform": "0.1.0",
486
+ "rightkit-agent-engine-api-sessions": "0.1.0",
487
+ "rightkit-agent-engine-api-workflows": "0.1.0",
488
+ "rightkit-agent-engine-attention": "0.1.0",
489
+ "rightkit-agent-engine-cli": "0.1.0",
490
+ "rightkit-agent-engine-core": "0.1.0",
491
+ "rightkit-agent-engine-history": "0.1.0",
492
+ "rightkit-agent-engine-mobile-runtime": "0.1.0",
493
+ "rightkit-agent-engine-protocol": "0.1.0",
494
+ "rightkit-agent-engine-runtime-middleware": "0.1.0",
495
+ "rightkit-agent-engine-runtime-resources": "0.1.0",
496
+ "rightkit-agent-engine-storage": "0.1.0",
497
+ "rightkit-agent-engine-tasks": "0.1.0",
498
+ "rightkit-agent-gateway": "0.1.0",
499
+ "rightkit-agent-host-traits": "0.1.0",
500
+ "rightkit-agent-orchestration": "0.1.1",
501
+ "rightkit-agent-orchestration-handoff": "0.1.0",
502
+ "rightkit-agent-plugins": "0.1.0",
503
+ "rightkit-agent-policy": "0.1.0",
504
+ "rightkit-agent-recipes": "0.1.0",
505
+ "rightkit-agent-tools": "0.3.1",
506
+ "rightkit-agent-transcript": "0.1.0",
507
+ "rightkit-agents": "0.2.1",
508
+ "rightkit-ai-accounts": "0.1.0",
460
509
  "rightkit-android": "0.3.0",
461
- "rightkit-app-settings": "0.3.0",
462
- "rightkit-asr": "0.3.1",
510
+ "rightkit-app-host": "0.1.0",
511
+ "rightkit-app-manager": "0.1.0",
512
+ "rightkit-app-settings": "0.3.1",
513
+ "rightkit-apple-ops": "0.1.1",
514
+ "rightkit-approvals": "0.1.0",
515
+ "rightkit-asr": "0.3.4",
463
516
  "rightkit-audio": "0.3.1",
464
- "rightkit-automation": "0.6.1",
517
+ "rightkit-autoedit": "0.1.1",
518
+ "rightkit-automation": "0.6.4",
519
+ "rightkit-bench": "0.1.0",
520
+ "rightkit-bot-messaging": "0.1.0",
465
521
  "rightkit-browser": "0.3.7",
466
522
  "rightkit-cache": "0.2.3",
523
+ "rightkit-captions": "0.1.0",
467
524
  "rightkit-channel-memory": "0.2.2",
525
+ "rightkit-chat-bridge": "0.1.0",
468
526
  "rightkit-child-process": "0.5.7",
469
- "rightkit-compose": "0.2.0",
527
+ "rightkit-code-audit-provider-sdk": "0.1.0",
528
+ "rightkit-color": "0.1.0",
529
+ "rightkit-compose": "0.2.2",
530
+ "rightkit-compositor": "0.1.0",
531
+ "rightkit-content-plan": "0.1.1",
532
+ "rightkit-content-proof": "0.1.0",
533
+ "rightkit-context": "0.1.0",
534
+ "rightkit-context-client": "0.1.0",
535
+ "rightkit-context-core": "0.1.0",
536
+ "rightkit-context-federation": "0.1.0",
537
+ "rightkit-context-protocol": "0.1.0",
538
+ "rightkit-context-provider-sdk": "0.1.0",
539
+ "rightkit-context-runtime": "0.1.0",
470
540
  "rightkit-contracts": "0.1.8",
471
541
  "rightkit-crash": "0.3.6",
472
- "rightkit-credentials": "0.4.2",
542
+ "rightkit-credentials": "0.4.3",
473
543
  "rightkit-crypto": "0.1.0",
474
- "rightkit-daemon": "0.5.0",
544
+ "rightkit-daemon": "0.5.1",
475
545
  "rightkit-db": "0.5.3",
476
546
  "rightkit-device": "0.4.0",
547
+ "rightkit-device-link-types": "0.1.0",
548
+ "rightkit-dictation-app-runtime": "0.1.0",
549
+ "rightkit-dictation-command-host": "0.1.0",
550
+ "rightkit-dictation-core": "0.2.0",
551
+ "rightkit-dictation-engine": "0.1.0",
552
+ "rightkit-dictation-inference": "0.1.0",
553
+ "rightkit-dictation-models": "0.1.0",
554
+ "rightkit-dictation-runtime": "0.1.0",
555
+ "rightkit-dictation-telemetry": "0.1.0",
556
+ "rightkit-dictation-text-pipeline": "0.1.0",
557
+ "rightkit-disk-cleanup": "0.1.0",
477
558
  "rightkit-docs": "0.1.3",
559
+ "rightkit-doctor": "0.1.0",
560
+ "rightkit-document-workspace": "0.1.1",
478
561
  "rightkit-embed": "0.6.2",
479
- "rightkit-ffmpeg": "0.2.11",
562
+ "rightkit-engine-host": "0.1.1",
563
+ "rightkit-evidence": "0.1.0",
564
+ "rightkit-evidence-contracts": "0.1.0",
565
+ "rightkit-evidence-decisions": "0.1.0",
566
+ "rightkit-evidence-effects": "0.1.0",
567
+ "rightkit-evidence-governance": "0.1.0",
568
+ "rightkit-evidence-report": "0.1.0",
569
+ "rightkit-ffmpeg": "0.2.13",
480
570
  "rightkit-file-watch": "0.4.2",
481
- "rightkit-fs": "0.4.7",
571
+ "rightkit-fs": "0.4.9",
482
572
  "rightkit-fsindex": "0.6.0",
573
+ "rightkit-github": "0.1.1",
574
+ "rightkit-google-apis": "0.1.0",
483
575
  "rightkit-google-identity": "0.2.0",
484
- "rightkit-hooks": "0.2.0",
576
+ "rightkit-hooks": "0.2.1",
485
577
  "rightkit-hotkeys": "0.1.2",
486
578
  "rightkit-http": "0.3.9",
487
579
  "rightkit-identity": "0.1.0",
488
580
  "rightkit-job-queue": "0.5.1",
581
+ "rightkit-library": "0.1.1",
489
582
  "rightkit-license": "0.2.6",
490
- "rightkit-llm": "0.3.1",
491
- "rightkit-logs": "0.3.6",
492
- "rightkit-mcp": "0.7.1",
493
- "rightkit-models": "0.5.2",
494
- "rightkit-motion": "0.2.0",
583
+ "rightkit-llm": "0.3.2",
584
+ "rightkit-local-gen": "0.1.0",
585
+ "rightkit-logs": "0.3.7",
586
+ "rightkit-mail": "0.1.1",
587
+ "rightkit-mcp": "0.7.2",
588
+ "rightkit-media-fetch": "0.1.1",
589
+ "rightkit-media-gen": "0.1.0",
590
+ "rightkit-media-pipeline": "0.1.0",
591
+ "rightkit-memory": "0.1.0",
592
+ "rightkit-memory-core": "0.1.0",
593
+ "rightkit-memory-store": "0.1.0",
594
+ "rightkit-mesh": "0.1.1",
595
+ "rightkit-models": "0.5.3",
596
+ "rightkit-motion": "0.2.1",
495
597
  "rightkit-oauth": "0.10.0",
496
598
  "rightkit-ocr": "0.2.3",
497
- "rightkit-onnx": "0.2.8",
599
+ "rightkit-onnx": "0.2.9",
498
600
  "rightkit-overlay": "0.2.0",
499
601
  "rightkit-overlay-win": "0.1.0",
500
- "rightkit-ownership": "0.4.3",
602
+ "rightkit-owner-approvals": "0.1.0",
603
+ "rightkit-ownership": "0.4.4",
501
604
  "rightkit-pdf": "0.3.4",
605
+ "rightkit-pipeline": "0.1.1",
502
606
  "rightkit-platform-rules": "0.1.0",
503
607
  "rightkit-playback": "0.3.0",
504
- "rightkit-qa": "0.10.0",
608
+ "rightkit-policy-rules": "0.1.0",
609
+ "rightkit-privileged-helper": "0.1.0",
610
+ "rightkit-qa": "0.10.1",
505
611
  "rightkit-qa-driver": "0.7.0",
506
612
  "rightkit-record": "0.3.3",
507
- "rightkit-review": "0.4.0",
508
- "rightkit-sandbox": "0.6.1",
613
+ "rightkit-reframe": "0.1.0",
614
+ "rightkit-remote-compute": "0.1.0",
615
+ "rightkit-remote-gateway": "0.1.0",
616
+ "rightkit-repo-graph": "0.1.0",
617
+ "rightkit-repo-graph-grammars": "0.1.0",
618
+ "rightkit-research": "0.1.1",
619
+ "rightkit-review": "0.4.1",
620
+ "rightkit-sandbox": "0.6.2",
621
+ "rightkit-scheduler": "0.1.0",
509
622
  "rightkit-search": "0.4.1",
510
623
  "rightkit-send": "0.3.0",
624
+ "rightkit-shorts": "0.1.1",
511
625
  "rightkit-sidecar": "0.3.0",
626
+ "rightkit-social-analytics": "0.1.1",
627
+ "rightkit-social-inbox": "0.1.1",
628
+ "rightkit-social-publish": "0.1.1",
512
629
  "rightkit-speech-client": "0.4.1",
513
630
  "rightkit-spend": "0.2.0",
514
631
  "rightkit-sync": "0.2.2",
632
+ "rightkit-system-monitor": "0.1.0",
515
633
  "rightkit-tauri-shell": "0.7.0",
516
- "rightkit-time": "0.1.0",
634
+ "rightkit-terminal-ui": "0.1.0",
635
+ "rightkit-text-polish": "0.1.0",
636
+ "rightkit-time": "0.1.1",
637
+ "rightkit-timeline": "0.1.0",
517
638
  "rightkit-transcript": "0.4.0",
639
+ "rightkit-transcript-library": "0.1.0",
518
640
  "rightkit-tts": "0.2.3",
519
641
  "rightkit-tts-qwen": "0.2.7",
520
642
  "rightkit-tts-qwen-sys": "0.1.2",
521
643
  "rightkit-typeset": "0.1.0",
522
644
  "rightkit-vad": "0.1.2",
523
645
  "rightkit-vault": "0.2.0",
524
- "rightkit-workflow": "0.2.0"
646
+ "rightkit-video-project": "0.1.0",
647
+ "rightkit-voice-commands": "0.1.0",
648
+ "rightkit-web-extract": "0.1.1",
649
+ "rightkit-web-session": "0.1.0",
650
+ "rightkit-workflow": "0.2.1"
525
651
  },
526
652
  "swift": {
527
653
  "rightkit-swift": {
@@ -801,7 +927,8 @@
801
927
  "0.3.5",
802
928
  "0.4.0",
803
929
  "0.5.0",
804
- "0.6.0"
930
+ "0.6.0",
931
+ "0.6.1"
805
932
  ],
806
933
  "rightkit-shell": [
807
934
  "0.1.0",
@@ -860,7 +987,8 @@
860
987
  "0.4.3",
861
988
  "0.4.4",
862
989
  "0.4.5",
863
- "0.4.6"
990
+ "0.4.6",
991
+ "0.4.7"
864
992
  ],
865
993
  "rightkit-service": [
866
994
  "0.1.0",
@@ -959,7 +1087,8 @@
959
1087
  "0.1.0",
960
1088
  "0.2.0",
961
1089
  "0.2.1",
962
- "0.3.0"
1090
+ "0.3.0",
1091
+ "0.3.1"
963
1092
  ],
964
1093
  "rightkit-daemon": [
965
1094
  "0.2.0",
@@ -990,7 +1119,8 @@
990
1119
  "0.1.3",
991
1120
  "0.1.4",
992
1121
  "0.1.5",
993
- "0.2.0"
1122
+ "0.2.0",
1123
+ "0.3.0"
994
1124
  ],
995
1125
  "rightkit-automation": [
996
1126
  "0.3.0",
@@ -998,7 +1128,8 @@
998
1128
  "0.4.1",
999
1129
  "0.4.4",
1000
1130
  "0.5.0",
1001
- "0.6.0"
1131
+ "0.6.0",
1132
+ "0.6.1"
1002
1133
  ],
1003
1134
  "rightkit-child-process": [
1004
1135
  "0.4.0",
@@ -1021,7 +1152,8 @@
1021
1152
  "0.2.1"
1022
1153
  ],
1023
1154
  "rightkit-compose": [
1024
- "0.1.0"
1155
+ "0.1.0",
1156
+ "0.2.0"
1025
1157
  ],
1026
1158
  "rightkit-contracts": [
1027
1159
  "0.1.5",
@@ -1042,7 +1174,8 @@
1042
1174
  "rightkit-app-settings": [
1043
1175
  "0.2.0",
1044
1176
  "0.2.3",
1045
- "0.2.4"
1177
+ "0.2.4",
1178
+ "0.3.0"
1046
1179
  ],
1047
1180
  "rightkit-ffmpeg": [
1048
1181
  "0.2.6",
@@ -1061,13 +1194,16 @@
1061
1194
  "0.2.1",
1062
1195
  "0.3.0",
1063
1196
  "0.4.0",
1064
- "0.4.1"
1197
+ "0.4.1",
1198
+ "0.4.2"
1065
1199
  ],
1066
1200
  "rightkit-review": [
1067
- "0.2.0"
1201
+ "0.2.0",
1202
+ "0.4.0"
1068
1203
  ],
1069
1204
  "rightkit-workflow": [
1070
- "0.1.0"
1205
+ "0.1.0",
1206
+ "0.2.0"
1071
1207
  ],
1072
1208
  "rightkit-device": [
1073
1209
  "0.1.3",
@@ -1127,7 +1263,8 @@
1127
1263
  "0.1.0"
1128
1264
  ],
1129
1265
  "rightkit-motion": [
1130
- "0.1.0"
1266
+ "0.1.0",
1267
+ "0.2.0"
1131
1268
  ],
1132
1269
  "rightkit-spend": [
1133
1270
  "0.1.1"
@@ -1,6 +1,10 @@
1
1
  {
2
2
  "schema": 1,
3
3
  "heldResource": {
4
+ "rightkit-agent-demo": {
5
+ "ceilings": null,
6
+ "requiredScale": null
7
+ },
4
8
  "rightkit-agent-tools": {
5
9
  "ceilings": null,
6
10
  "requiredScale": null
@@ -58,7 +58,11 @@ import os from "node:os";
58
58
  import path from "node:path";
59
59
  import { fileURLToPath } from "node:url";
60
60
  import { spawnSync } from "./background-process.mjs";
61
- import { parse } from "smol-toml";
61
+ import { createRequire } from "node:module";
62
+ // smol-toml loads on first parse, not at import: CI jobs whose closure reaches this module without installing
63
+ // @rightkit/release dependencies (publish.yml crates pack, bind) never parse TOML here (rightkit#1341).
64
+ let smolTomlParse;
65
+ const parse = (text) => (smolTomlParse ??= createRequire(import.meta.url)("smol-toml").parse)(text);
62
66
  import { RUNTIME_BUDGET_CRATES } from "./runtime-budget-crates.mjs";
63
67
  import { singleHostOfPolicy } from "./publication-gate.mjs";
64
68
  import { findWorkspaceRoot, lockClosure, pathDependencyDirs, readCargoPackage } from "./crate-graph.mjs";
@@ -26,7 +26,7 @@ import fs from "node:fs";
26
26
  import path from "node:path";
27
27
  import { fileURLToPath } from "node:url";
28
28
 
29
- const SKIPPED_TREE = /(^|\/)(node_modules|vendor|target)\//;
29
+ const SKIPPED_TREE = /(^|\/)(node_modules|vendor|target|tests\/fixtures)\//;
30
30
  const PLATFORMS = ["macos", "windows", "linux", "ios", "android"];
31
31
  // The shell is a desktop plugin: a mobile window is created by the OS and shown by it, so R1 judges desktop targets only.
32
32
  const DESKTOP = new Set(["base", "macos", "windows", "linux"]);
@@ -51,7 +51,12 @@ export const brokerOutputRoots = ({ dataRoot }) => [path.join(dataRoot, "rightki
51
51
 
52
52
  /** The broker's receipt of one request, read through the RustRight client (`rustright status <id>`), synchronously. */
53
53
  export function receiptSync({ client, requestId, env, spawn = spawnSync, platform = process.platform }) {
54
- const r = spawnBrokerClient(spawn, client, ["status", requestId], { encoding: "utf8", env, timeout: 60_000, maxBuffer: 64 * 1024 * 1024 }, { platform });
54
+ return receiptFromReply(spawnBrokerClient(spawn, client, ["status", requestId], statusOptions(env), { platform }), requestId);
55
+ }
56
+ const statusOptions = (env) => ({ encoding: "utf8", env, timeout: 60_000, maxBuffer: 64 * 1024 * 1024 });
57
+
58
+ /** The receipt one `rustright status <id>` reply carries, or the typed throw (shared by the synchronous and the asynchronous read). */
59
+ function receiptFromReply(r, requestId) {
55
60
  const failedRun = () => new SnapshotRequestError("receipt", `rustright status ${requestId} failed (${r.error?.message ?? `exit ${r.status}`})`);
56
61
  if (r.error) throw failedRun();
57
62
  let doc;
@@ -165,7 +170,9 @@ function markCarriedRequest({ section, env, callHandle }) {
165
170
  }
166
171
 
167
172
  /** The spawn errors that prove the client process never started, so no byte reached the broker (GB-21). Anything else after a start is ambiguous and is never marked unsent. */
168
- const NO_SPAWN_CODES = new Set(["ENOENT", "EACCES", "EPERM", "EINVAL", "ENOEXEC", "ENOTDIR", BROKER_CLIENT_UNRESOLVED]);
173
+ /** The code of a carried call whose abort signal fired before the client was started (the asynchronous carry): proof that no byte reached the broker. */
174
+ export const CLIENT_CANCELLED_BEFORE_SPAWN = "RK_CLIENT_CANCELLED_BEFORE_SPAWN";
175
+ const NO_SPAWN_CODES = new Set(["ENOENT", "EACCES", "EPERM", "EINVAL", "ENOEXEC", "ENOTDIR", BROKER_CLIENT_UNRESOLVED, CLIENT_CANCELLED_BEFORE_SPAWN]);
169
176
  export const provenNoSpawn = (error) => Boolean(error) && NO_SPAWN_CODES.has(error.code);
170
177
 
171
178
  /**
@@ -268,6 +275,8 @@ export function inheritedCarrierEnv(env) {
268
275
  * handle durably complete); ACTIVE, UNKNOWN, a wait that timed out, a reader that is absent or throws all leave ownership held, and the outcome says which. Never resubmits.
269
276
  */
270
277
  function reconcileAfterCancel({ section, callHandle, childEnv }) {
278
+ // This read BLOCKS the event loop for up to 30 s (a synchronous client call). Only the synchronous carry (carryBrokerAdapter, a stage step that blocks anyway) reaches it: the
279
+ // asynchronous carry (a Swift scan next to the gate's other scans) never calls it, because it must not freeze them; it reports the sub-handle UNKNOWN instead (deferredReconcile).
271
280
  const cancelWait = Number(childEnv?.RUSTRIGHT_CANCEL_WAIT_MS);
272
281
  const waitMs = Math.min(30_000, Number.isFinite(cancelWait) && cancelWait >= 0 ? Math.trunc(cancelWait) : 30_000);
273
282
  if (typeof section.leaseStatus !== "function") return { reaped: false, subHandle: { handle: callHandle, state: "UNKNOWN", why: "this gate has no lease reader" }, note: `the gate cannot ask the broker about sub-handle ${callHandle}, so ownership stays UNKNOWN until every sub-handle is REAPED` };
@@ -279,6 +288,11 @@ function reconcileAfterCancel({ section, callHandle, childEnv }) {
279
288
  return { reaped: false, subHandle, note: `after the cancel the broker reports sub-handle ${callHandle} still ${state}${status?.why ? ` (${status.why})` : ""}: ownership stays held until it is REAPED; the job is not resubmitted` };
280
289
  }
281
290
 
291
+ /** What the asynchronous carry reports after a cancel or an operation deadline instead of the blocking lease read: ownership stays held, fail closed, and is settled when every sub-handle is REAPED. */
292
+ function deferredReconcile(callHandle) {
293
+ return { reaped: false, subHandle: { handle: callHandle, state: "UNKNOWN", why: "the asynchronous carry reads no lease: the lease reader blocks the event loop for up to 30 s" }, note: `the asynchronous carry does not read the broker's lease (that read blocks the gate for up to 30 s), so sub-handle ${callHandle} stays UNKNOWN and ownership stays held until every sub-handle is REAPED; the job is not resubmitted` };
294
+ }
295
+
282
296
  /**
283
297
  * GB-52, GB-78, GB-39 (BROKER-CONTRACT section 16): a carried call the broker answered with a refusal that clears on its own (RK_RECOVERY_PENDING, RK_STORAGE_BUDGET_UNAVAILABLE) or whose
284
298
  * connection never opened (RK_BROKER_BUSY: every pipe instance busy, no byte sent) is a wait. The gate pauses (250 ms doubling to 5 s) and sends the call again within the call's own
@@ -370,7 +384,7 @@ function carryBrokerCargoOnce({ words, cwd, env, section, workspace, checkoutWor
370
384
  const nonce = /^[0-9a-f]{64}$/.test(stageNonce ?? "") ? carriedCallNonce({ stageNonce, cwd: path.relative(section.checkout, checkoutCwd).split(path.sep).join("/") || ".", argv: words }) : undefined;
371
385
  made = writeSnapshotRequest({ controlRoot: section.controlRoot, commit: section.commit, snapshot: section.snapshot, declaredInputs: inputs, handle: section.handle, primaryCwd: checkoutCwd, argv: words, nonce, requestIdFile: path.join(refDir, "request.json") });
372
386
  } catch (e) { return refuse("RK_CARGO_BROKER_CARRIER", e.message); }
373
- try { record({ ...base, status: "pending", nonce: made.carrier.nonce, ownershipHandle: made.carrier.ownership.handle, checkoutCwd, spawnCwd }); } catch (e) { removeCarrier(made.file); return refuse("RK_CARGO_LEDGER_ERROR", `the call cannot be recorded (${e.message}); it does not run`); }
387
+ try { record({ ...base, status: "pending", nonce: made.carrier.nonce, requestRef: path.join(refDir, "request.json"), ownershipHandle: made.carrier.ownership.handle, checkoutCwd, spawnCwd }); } catch (e) { removeCarrier(made.file); return refuse("RK_CARGO_LEDGER_ERROR", `the call cannot be recorded (${e.message}); it does not run`); }
374
388
  // GB-52: the call's operation deadline (the caller's own cap is never replaced) and an outer guard past the client's own cancel wait.
375
389
  const childEnv = { ...clientEnv, ...made.env, ...operationDeadlineEnv({ env: clientEnv, deadlineMs }) };
376
390
  const guard = clientGuardMs(childEnv);
@@ -450,7 +464,82 @@ function carryBrokerCargoOnce({ words, cwd, env, section, workspace, checkoutWor
450
464
  */
451
465
  export function carryBrokerAdapter(call) { return carryWithRetries(carryBrokerAdapterOnce, call); }
452
466
 
453
- function carryBrokerAdapterOnce({ adapter, words, cwd, env, section, record, spawn = spawnSync, err, tmp = os.tmpdir(), stdio = "inherit", deadlineMs = null, ledgerId = null }) {
467
+ /**
468
+ * The same carried adapter call without blocking the caller's event loop (rightkit#1367 step 2: a carried Swift scan used to freeze the gate for as long as it ran or
469
+ * queued). One body (carryBrokerAdapterSteps) serves both: it yields each client call (`{ args, options }`) and the driver runs it, `spawnSync` for the synchronous call and
470
+ * an awaited asynchronous spawn here. `signal` (an AbortSignal) is handed to the client's spawn, so the scan's deadline or a cancel stops the client's process tree; a
471
+ * retry wait is abortable too. The lease reconciliation after an operation deadline stays synchronous (a bounded read, never reached on the normal path).
472
+ */
473
+ export async function carryBrokerAdapterAsync(call) {
474
+ const { spawn, signal = null, deadlineMs = null, now = Date.now, sleep = abortableSleep, err = () => {}, record = () => {}, ...rest } = call;
475
+ if (typeof spawn !== "function") throw new TypeError("carryBrokerAdapterAsync needs an asynchronous spawn");
476
+ const window = deadlineMs !== null && Number.isFinite(deadlineMs) ? Math.max(1, Math.trunc(deadlineMs)) : DEFAULT_RETRY_WINDOW_MS;
477
+ const started = now();
478
+ const ledgerId = crypto.randomUUID();
479
+ const drive = async (effect) => {
480
+ // An abort that arrived before the client was started proves nothing was sent: the error carries the code provenNoSpawn recognises, so the sub-handle is recorded unsent.
481
+ if (signal?.aborted && effect.args[0] === "run") return { status: null, stdout: "", stderr: "", cancelled: true, error: Object.assign(new Error("cancelled before the client started"), { code: CLIENT_CANCELLED_BEFORE_SPAWN }) };
482
+ return spawnBrokerClient(spawn, rest.section.client, effect.args, { ...effect.options, ...(signal ? { signal } : {}) }, { platform: rest.section.platform });
483
+ };
484
+ let attempt = 0; let held = []; let result;
485
+ for (;;) {
486
+ attempt += 1;
487
+ held = [];
488
+ const remaining = Math.max(1, window - (now() - started));
489
+ result = await runStepsAsync(carryBrokerAdapterSteps({ ...rest, record, err: (m) => held.push(m), deadlineMs: deadlineMs === null ? null : remaining, ledgerId, signal, asyncCarry: true }), drive);
490
+ const code = result.outcome?.status === "unverified" ? result.outcome.code : null;
491
+ if (code === null || !RETRYABLE_OUTCOME_CODES.has(code) || signal?.aborted) break;
492
+ const pause = retryPauseMs(attempt);
493
+ if (window - (now() - started) <= pause) {
494
+ const elapsed = now() - started;
495
+ const message = `${result.outcome.message}; retried ${attempt - 1} time(s) over ${elapsed} ms and the call's ${window} ms deadline passed before the broker would take it: it is a wait that did not clear, never a result`;
496
+ result = { ...result, outcome: { ...result.outcome, message, attempts: attempt, retried: attempt - 1 } };
497
+ try { record({ id: ledgerId, mode: "broker", status: "unverified", code: result.outcome.code, message }); } catch { /* the final record above stands */ }
498
+ held.push(`rightkit-gate: ${result.outcome.code}: ${message}`);
499
+ break;
500
+ }
501
+ await sleep(pause, signal);
502
+ // A cancel during the wait ends the carry here: nothing is marked or sent for another attempt (the refused attempt was already recorded as settled or unsent).
503
+ if (signal?.aborted) break;
504
+ try { record({ id: ledgerId, mode: "broker", status: "pending", code: null, message: null }); } catch { /* the attempt records its own pending state */ }
505
+ }
506
+ for (const m of held) err(m);
507
+ return result;
508
+ }
509
+
510
+ /** A retry wait that ends at once when the abort signal fires (the default `sleep` of the asynchronous carry). */
511
+ function abortableSleep(ms, signal = null) {
512
+ return new Promise((resolve) => {
513
+ if (signal?.aborted) { resolve(); return; }
514
+ const done = () => { clearTimeout(timer); signal?.removeEventListener("abort", done); resolve(); };
515
+ const timer = setTimeout(done, ms);
516
+ signal?.addEventListener("abort", done, { once: true });
517
+ });
518
+ }
519
+
520
+ /** Drives a steps generator synchronously: each yielded client call runs through `drive` (spawnSync or its test double). */
521
+ function runStepsSync(steps, drive) {
522
+ let step = steps.next();
523
+ while (!step.done) step = steps.next(drive(step.value));
524
+ return step.value;
525
+ }
526
+ async function runStepsAsync(steps, drive) {
527
+ let step = steps.next();
528
+ while (!step.done) {
529
+ let reply;
530
+ try { reply = await drive(step.value); } catch (error) { step = steps.throw(error); continue; }
531
+ step = steps.next(reply);
532
+ }
533
+ return step.value;
534
+ }
535
+
536
+ function carryBrokerAdapterOnce(call) {
537
+ const { section, spawn = spawnSync } = call;
538
+ return runStepsSync(carryBrokerAdapterSteps(call), (effect) => spawnBrokerClient(spawn, section.client, effect.args, effect.options, { platform: section.platform }));
539
+ }
540
+
541
+ /** One carried adapter attempt as a generator: it yields `{ args, options }` for each client call (the carried run, then the receipt's status) and receives the spawn result. */
542
+ function* carryBrokerAdapterSteps({ adapter, words, cwd, env, section, record, err, tmp = os.tmpdir(), stdio = "inherit", deadlineMs = null, ledgerId = null, signal = null, asyncCarry = false }) {
454
543
  const id = ledgerId ?? crypto.randomUUID();
455
544
  words = [...words].map(String);
456
545
  const base = { id, mode: "broker", shim: "adapter", adapter, cwd, argv: [...words] };
@@ -493,15 +582,17 @@ function carryBrokerAdapterOnce({ adapter, words, cwd, env, section, record, spa
493
582
  const nonce = /^[0-9a-f]{64}$/.test(stageNonce ?? "") ? carriedCallNonce({ stageNonce, cwd: path.relative(section.checkout, checkoutCwd).split(path.sep).join("/") || ".", argv: words }) : undefined;
494
583
  made = writeSnapshotRequest({ controlRoot: section.controlRoot, commit: section.commit, snapshot: section.snapshot, declaredInputs: inputs, handle: section.handle, primaryCwd: checkoutCwd, argv: words, nonce, requestIdFile: path.join(refDir, "request.json") });
495
584
  } catch (e) { return refuse("RK_ADAPTER_BROKER_CARRIER", e.message); }
496
- try { record({ ...base, status: "pending", nonce: made.carrier.nonce, ownershipHandle: made.carrier.ownership.handle, checkoutCwd, spawnCwd }); } catch (e) { removeCarrier(made.file); return refuse("RK_CARGO_LEDGER_ERROR", `the call cannot be recorded (${e.message}); it does not run`); }
585
+ try { record({ ...base, status: "pending", nonce: made.carrier.nonce, requestRef: path.join(refDir, "request.json"), ownershipHandle: made.carrier.ownership.handle, checkoutCwd, spawnCwd }); } catch (e) { removeCarrier(made.file); return refuse("RK_CARGO_LEDGER_ERROR", `the call cannot be recorded (${e.message}); it does not run`); }
497
586
  // GB-52: the call's operation deadline (the caller's own cap is never replaced) and an outer guard past the client's own cancel wait.
498
587
  const childEnv = { ...clientEnv, ...made.env, ...operationDeadlineEnv({ env: clientEnv, deadlineMs }) };
499
588
  const guard = clientGuardMs(childEnv);
500
589
  const capture = stdio === "capture";
501
590
  // Broker work may exist from the next statement on: the ownership records this request's sub-handle first (a mark that does not land sends nothing).
502
591
  { const startable = clientStartable(section.client, section.platform); if (startable) { removeCarrier(made.file); return refuse("RK_ADAPTER_BROKER_CLIENT", `${startable}; nothing was marked or sent`); } }
592
+ // A scan cancelled before the request is marked sends nothing and marks nothing (the mark records that broker work may exist).
593
+ if (signal?.aborted) { removeCarrier(made.file); return refuse("RK_ADAPTER_BROKER_CANCELLED", "the scan was cancelled before the request was marked or sent; nothing was sent"); }
503
594
  try { markCarriedRequest({ section, env, callHandle: made.carrier.ownership.handle }); } catch (e) { removeCarrier(made.file); return refuse("RK_ADAPTER_BROKER_OWNERSHIP", e.message); }
504
- const r = spawnBrokerClient(spawn, section.client, ["run", adapter, "--", ...words], capture ? { cwd: spawnCwd, env: childEnv, encoding: "utf8", maxBuffer: 1 << 28, windowsHide: true, ...(guard ? { timeout: guard } : {}) } : { cwd: spawnCwd, env: childEnv, stdio: "inherit", ...(guard ? { timeout: guard } : {}) }, { platform: section.platform });
595
+ const r = yield { args: ["run", adapter, "--", ...words], options: capture ? { cwd: spawnCwd, env: childEnv, encoding: "utf8", maxBuffer: 1 << 28, windowsHide: true, ...(guard ? { timeout: guard } : {}) } : { cwd: spawnCwd, env: childEnv, stdio: "inherit", ...(guard ? { timeout: guard } : {}) } };
505
596
  removeCarrier(made.file);
506
597
  // GB-21: a client that provably never started sent nothing: that sub-handle is recorded unsent under the ownership lock, so it does not wedge a later reclaim. Any other
507
598
  // failure after a start (a lost connection, a timeout, an exit before a reference) is ambiguous and stays held, reconciled by the request id.
@@ -509,7 +600,13 @@ function carryBrokerAdapterOnce({ adapter, words, cwd, env, section, record, spa
509
600
  let outcome;
510
601
  let ref = null;
511
602
  let nativeManaged = null;
603
+ const cancelledRun = Boolean(r.error) && (r.cancelled === true || signal?.aborted === true);
512
604
  try {
605
+ if (r.error && cancelledRun) {
606
+ // The scan's own signal stopped the client: a cancel, never "the client did not start" (the request may exist at the broker; its reference is kept when the client wrote it).
607
+ try { ref = readRequestRef(path.join(refDir, "request.json")); } catch { ref = null; }
608
+ throw new SnapshotRequestError("cancelled", provenNoSpawn(r.error) ? "the scan was cancelled before the RustRight client started; nothing was sent" : `the scan was cancelled: its signal stopped the RustRight client${ref ? ` (request ${ref.requestId})` : ""}; the request is not resubmitted`);
609
+ }
513
610
  if (r.error) throw new SnapshotRequestError(r.error.code === BROKER_CLIENT_UNRESOLVED ? BROKER_CLIENT_UNRESOLVED : "spawn", `the RustRight client did not start (${r.error.message})`);
514
611
  ref = readRequestRef(path.join(refDir, "request.json"));
515
612
  // The request's workspace of a snapshot-bound adapter job is the directory the client ran in: the primary checkout's counterpart of the step's
@@ -517,7 +614,8 @@ function carryBrokerAdapterOnce({ adapter, words, cwd, env, section, record, spa
517
614
  // directory in the snapshot), which must be inside that root"). It is the carrier's primaryCwd, never the toplevel for a step run from a subfolder
518
615
  // (an app whose root is `app/`), and never a directory outside the primary checkout or one that is not this call's counterpart.
519
616
  if (canonicalCounterpart(ref.workspace) !== canonicalCounterpart(checkoutCwd) || !inside(canonicalCounterpart(ref.workspace), section.checkout)) throw new SnapshotRequestError("workspace", `the request's workspace ${ref.workspace} is not the primary checkout counterpart ${checkoutCwd} of the step's directory ${realCwd} (primary toplevel ${section.checkout})`);
520
- const receipt = receiptSync({ client: section.client, requestId: ref.requestId, env: clientEnv, spawn, platform: section.platform });
617
+ const statusReply = yield { args: ["status", ref.requestId], options: statusOptions(clientEnv) };
618
+ const receipt = receiptFromReply(statusReply, ref.requestId);
521
619
  const { payload, evidence, attribution, toolchain, nestedChildren } = verifySnapshotReceipt(receipt, { pins: section.pins, jobId: ref.requestId, carrier: made.carrier, argv: words, workspace: ref.workspace, adapter, adapterEnv, checkout: section.checkout, outputRoots: section.outputRoots, platform: section.platform });
522
620
  nativeManaged = signedNativeOutputs(receipt, payload);
523
621
  outcome = { status: "verified", requestId: ref.requestId, workspace: ref.workspace, outputDir: evidence.outputDir, nestedChildren, treeAtStart: evidence.snapshot.treeAtStart, treeAtExit: evidence.snapshot.treeAtExit, attribution, toolchain };
@@ -538,8 +636,8 @@ function carryBrokerAdapterOnce({ adapter, words, cwd, env, section, record, spa
538
636
  const seen = operationOutcomeInOutput(`${out.stdout}\n${out.stderr}`) ?? (r.error?.code === "ETIMEDOUT" ? { code: "operation-deadline", deadlineMs: null, requestId: ref?.requestId ?? null, cancelConfirmed: null, terminalState: null, guard: true } : null);
539
637
  if (seen) outcome = { status: "unverified", code: seen.code, message: seen.code === "operation-deadline" ? `the operation deadline${seen.deadlineMs ? ` of ${seen.deadlineMs} ms` : ""} passed${seen.guard ? " and the client was stopped by the gate's outer guard" : ""}; request ${seen.requestId ?? ref?.requestId ?? "unknown"}: cancelConfirmed: ${seen.cancelConfirmed ?? "unknown"}, terminal state: ${seen.terminalState ?? "not observed"}; the job is neither abandoned nor resubmitted and ownership stays UNKNOWN until every sub-handle is REAPED` : seen.code === "broker-busy" ? "every broker connection was busy (RK_BROKER_BUSY: a wait, not an outage); nothing was admitted" : "the broker was unavailable (RK_BROKER_UNAVAILABLE)", ...(ref ? { requestId: ref.requestId, workspace: ref.workspace } : {}) };
540
638
  }
541
- if (outcome.status === "unverified" && outcome.code === "operation-deadline") {
542
- const settled = reconcileAfterCancel({ section, callHandle: made.carrier.ownership.handle, childEnv });
639
+ if (outcome.status === "unverified" && (outcome.code === "operation-deadline" || (outcome.code === "cancelled" && !provenNoSpawn(r.error)))) {
640
+ const settled = asyncCarry ? deferredReconcile(made.carrier.ownership.handle) : reconcileAfterCancel({ section, callHandle: made.carrier.ownership.handle, childEnv });
543
641
  outcome = { ...outcome, message: `${outcome.message}; ${settled.note}`, reaped: settled.reaped, subHandle: settled.subHandle };
544
642
  }
545
643
  // RK_BROKER_BUSY with no request reference: the connection never opened, so no byte reached the broker. That is the gate's own proof that this sub-handle was never sent (GB-21), recorded under
@@ -231,6 +231,8 @@ export function installSnapshotDependencies({ checkout, snapshot, commit, tree,
231
231
  const relative = path.relative(base, path.resolve(base, selected)).split(path.sep).join("/");
232
232
  const actual = manifests.get(fold(relative));
233
233
  const owner = nearestLock(fold(relative));
234
+ // A package under an install root that commits no pnpm-lock.yaml has nothing to install from: not an error here (the root loop below records it as skipped and the pnpm-lockfile scan judges it).
235
+ if (actual !== undefined && owner === undefined && [...installRoots].some((r) => !r || fold(relative) === r || fold(relative).startsWith(`${r}/`))) continue;
234
236
  if (actual === undefined || owner === undefined || !installRoots.has(owner) || /[*?\[\]{}!^()\\]/.test(actual) || actual.includes("...")) throw new CheckError(`dependency package ${selected} is not an exact committed manifest directory owned by an install root`);
235
237
  if (!selectedByRoot.has(owner)) selectedByRoot.set(owner, new Set());
236
238
  selectedByRoot.get(owner).add(actual);