@team-agent/installer 0.5.60 → 0.5.62

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 (265) hide show
  1. package/Cargo.lock +1 -1
  2. package/Cargo.toml +1 -1
  3. package/crates/team-agent/src/cli/adapters.rs +49 -3
  4. package/crates/team-agent/src/cli/attach_app_server_leader.rs +1 -0
  5. package/crates/team-agent/src/cli/diagnose.rs +184 -0
  6. package/crates/team-agent/src/cli/emit.rs +104 -21
  7. package/crates/team-agent/src/cli/helpers.rs +1 -0
  8. package/crates/team-agent/src/cli/leader.rs +14 -2
  9. package/crates/team-agent/src/cli/leaders.rs +2 -0
  10. package/crates/team-agent/src/cli/mod.rs +296 -8
  11. package/crates/team-agent/src/cli/named_address.rs +26 -2
  12. package/crates/team-agent/src/cli/profile.rs +2 -1
  13. package/crates/team-agent/src/cli/send/coordinator.rs +1 -0
  14. package/crates/team-agent/src/cli/send/mailbox.rs +3 -0
  15. package/crates/team-agent/src/cli/send/persist.rs +1 -0
  16. package/crates/team-agent/src/cli/send/presentation.rs +3 -0
  17. package/crates/team-agent/src/cli/send/resolve.rs +206 -1
  18. package/crates/team-agent/src/cli/send.rs +2 -1
  19. package/crates/team-agent/src/cli/spec.rs +5 -1
  20. package/crates/team-agent/src/cli/status.rs +2 -1
  21. package/crates/team-agent/src/cli/status_port/compact.rs +1 -0
  22. package/crates/team-agent/src/cli/status_port/format.rs +1 -0
  23. package/crates/team-agent/src/cli/status_port/inbox.rs +1 -0
  24. package/crates/team-agent/src/cli/status_port/store.rs +7 -3
  25. package/crates/team-agent/src/cli/tests/named_address.rs +65 -0
  26. package/crates/team-agent/src/cli/tests/verb_profile.rs +38 -0
  27. package/crates/team-agent/src/cli/types.rs +36 -2
  28. package/crates/team-agent/src/communication_mode/mod.rs +54 -0
  29. package/crates/team-agent/src/compiler/tests.rs +5 -5
  30. package/crates/team-agent/src/compiler.rs +54 -1
  31. package/crates/team-agent/src/conpty/backend.rs +1 -0
  32. package/crates/team-agent/src/conpty/mod.rs +1 -0
  33. package/crates/team-agent/src/coordinator/backoff.rs +5 -8
  34. package/crates/team-agent/src/coordinator/conpty_shim.rs +3 -2
  35. package/crates/team-agent/src/coordinator/health.rs +1 -0
  36. package/crates/team-agent/src/coordinator/mod.rs +2 -2
  37. package/crates/team-agent/src/coordinator/orphan.rs +4 -116
  38. package/crates/team-agent/src/coordinator/runtime_detectors.rs +1 -0
  39. package/crates/team-agent/src/coordinator/runtime_observation.rs +1 -0
  40. package/crates/team-agent/src/coordinator/steps/abnormal.rs +6 -5
  41. package/crates/team-agent/src/coordinator/steps/delivery.rs +1 -0
  42. package/crates/team-agent/src/coordinator/steps/health_sync.rs +1 -0
  43. package/crates/team-agent/src/coordinator/steps/mod.rs +2 -0
  44. package/crates/team-agent/src/coordinator/steps/persist.rs +1 -0
  45. package/crates/team-agent/src/coordinator/steps/runtime_prompts.rs +1 -0
  46. package/crates/team-agent/src/coordinator/steps/session_gate.rs +1 -0
  47. package/crates/team-agent/src/coordinator/tests/abnormal.rs +0 -174
  48. package/crates/team-agent/src/coordinator/tests/api_error_recovery.rs +0 -9
  49. package/crates/team-agent/src/coordinator/tests/basics.rs +0 -184
  50. package/crates/team-agent/src/coordinator/tests/mod.rs +2 -17
  51. package/crates/team-agent/src/coordinator/tests/tick_core.rs +0 -42
  52. package/crates/team-agent/src/coordinator/tick.rs +24 -28
  53. package/crates/team-agent/src/coordinator/types.rs +3 -176
  54. package/crates/team-agent/src/db/agent_health_capture.rs +1 -0
  55. package/crates/team-agent/src/db/message_store.rs +47 -2
  56. package/crates/team-agent/src/db/migration.rs +5 -4
  57. package/crates/team-agent/src/db/mod.rs +1 -0
  58. package/crates/team-agent/src/db/schema.rs +1 -0
  59. package/crates/team-agent/src/diagnose/comms.rs +2 -1
  60. package/crates/team-agent/src/diagnose/mod.rs +1 -0
  61. package/crates/team-agent/src/diagnose/orphans.rs +1 -0
  62. package/crates/team-agent/src/fake_worker.rs +21 -1
  63. package/crates/team-agent/src/layout/manager.rs +3 -7
  64. package/crates/team-agent/src/layout/mod.rs +2 -2
  65. package/crates/team-agent/src/layout/overlay.rs +6 -1
  66. package/crates/team-agent/src/layout/placement.rs +1 -0
  67. package/crates/team-agent/src/layout/recovery.rs +3 -0
  68. package/crates/team-agent/src/layout/runtime_sessions.rs +8 -3
  69. package/crates/team-agent/src/layout/sessions.rs +8 -1
  70. package/crates/team-agent/src/layout/tmux_endpoint.rs +3 -0
  71. package/crates/team-agent/src/layout/worker_env.rs +3 -0
  72. package/crates/team-agent/src/layout/worker_window_helpers.rs +2 -0
  73. package/crates/team-agent/src/leader/helpers.rs +1 -0
  74. package/crates/team-agent/src/leader/incident.rs +1 -0
  75. package/crates/team-agent/src/leader/lease.rs +27 -22
  76. package/crates/team-agent/src/leader/mod.rs +2 -3
  77. package/crates/team-agent/src/leader/owner_bind.rs +13 -3
  78. package/crates/team-agent/src/leader/provider_attribution.rs +1 -0
  79. package/crates/team-agent/src/leader/rediscover.rs +4 -17
  80. package/crates/team-agent/src/leader/registry.rs +3 -15
  81. package/crates/team-agent/src/leader/start.rs +1072 -186
  82. package/crates/team-agent/src/leader/takeover.rs +10 -232
  83. package/crates/team-agent/src/leader/tests/claim_leader_mailbox_flush_contract.rs +544 -0
  84. package/crates/team-agent/src/leader/tests/historical_inventory_upgrade_gate_red.rs +624 -0
  85. package/crates/team-agent/src/leader/tests/identity.rs +71 -59
  86. package/crates/team-agent/src/leader/tests/identity_session_names.rs +42 -0
  87. package/crates/team-agent/src/leader/tests/idle.rs +0 -193
  88. package/crates/team-agent/src/leader/tests/lease_api.rs +0 -45
  89. package/crates/team-agent/src/leader/tests/mod.rs +4 -41
  90. package/crates/team-agent/src/leader/tests/rehomed_env.rs +83 -0
  91. package/crates/team-agent/src/leader/tests/team_in_team_state_scope_red.rs +1273 -0
  92. package/crates/team-agent/src/leader/tests/terminal_and_replay_vs_rerender_invariants_red.rs +417 -0
  93. package/crates/team-agent/src/leader/tests/wake_start_owner.rs +22 -103
  94. package/crates/team-agent/src/leader/types.rs +1 -28
  95. package/crates/team-agent/src/lib.rs +7 -0
  96. package/crates/team-agent/src/lifecycle/display.rs +7 -1
  97. package/crates/team-agent/src/lifecycle/helpers.rs +3 -1
  98. package/crates/team-agent/src/lifecycle/launch/add_agent.rs +2 -2
  99. package/crates/team-agent/src/lifecycle/launch/fork_agent/completion.rs +1 -0
  100. package/crates/team-agent/src/lifecycle/launch/fork_agent.rs +1 -1
  101. package/crates/team-agent/src/lifecycle/launch/fork_entry.rs +1 -0
  102. package/crates/team-agent/src/lifecycle/launch/fork_finalize.rs +1 -0
  103. package/crates/team-agent/src/lifecycle/launch/plan.rs +2 -2
  104. package/crates/team-agent/src/lifecycle/launch/quick_start.rs +4 -26
  105. package/crates/team-agent/src/lifecycle/launch/readiness.rs +0 -27
  106. package/crates/team-agent/src/lifecycle/launch/spawn.rs +2 -28
  107. package/crates/team-agent/src/lifecycle/launch/spec_state.rs +4 -32
  108. package/crates/team-agent/src/lifecycle/launch/state_projection.rs +1 -0
  109. package/crates/team-agent/src/lifecycle/launch.rs +11 -12
  110. package/crates/team-agent/src/lifecycle/mod.rs +2 -1
  111. package/crates/team-agent/src/lifecycle/restart/agent.rs +4 -4
  112. package/crates/team-agent/src/lifecycle/restart/common.rs +1 -1
  113. package/crates/team-agent/src/lifecycle/restart/orchestrator.rs +2 -2
  114. package/crates/team-agent/src/lifecycle/restart/rebuild.rs +13 -5
  115. package/crates/team-agent/src/lifecycle/restart/remove.rs +1 -1
  116. package/crates/team-agent/src/lifecycle/restart/selection.rs +3 -3
  117. package/crates/team-agent/src/lifecycle/restart.rs +13 -11
  118. package/crates/team-agent/src/lifecycle/tests/acceptance_b_batch_red.rs +1007 -0
  119. package/crates/team-agent/src/lifecycle/tests/b0_legacy_snapshot_nonauthority_contract.rs +265 -0
  120. package/crates/team-agent/src/lifecycle/tests/b0_reader_hideone_audit_contract.rs +459 -0
  121. package/crates/team-agent/src/lifecycle/tests/behavioral_diff_264_red.rs +849 -0
  122. package/crates/team-agent/src/lifecycle/tests/claude_compatible_config_red.rs +323 -0
  123. package/crates/team-agent/src/lifecycle/tests/claude_profile_launch_red.rs +423 -0
  124. package/crates/team-agent/src/lifecycle/tests/clone_fork_copilot_perms_red.rs +737 -0
  125. package/crates/team-agent/src/lifecycle/tests/codex_mcp_approval_inheritance_red.rs +1187 -0
  126. package/crates/team-agent/src/lifecycle/tests/codex_weak_window_attribution_red.rs +683 -0
  127. package/crates/team-agent/src/lifecycle/tests/communication_mode_runtime_contract_red.rs +394 -0
  128. package/crates/team-agent/src/lifecycle/tests/copilot_provider_red.rs +1856 -0
  129. package/crates/team-agent/src/lifecycle/tests/core_034_real_red.rs +917 -0
  130. package/crates/team-agent/src/lifecycle/tests/display_adaptive_red.rs +481 -0
  131. package/crates/team-agent/src/lifecycle/tests/f032_startup_prompt_best_effort_red.rs +301 -0
  132. package/crates/team-agent/src/lifecycle/tests/harvest2_a_batch_red.rs +516 -0
  133. package/crates/team-agent/src/lifecycle/tests/host_cotenant_death_p0_contract.rs +986 -0
  134. package/crates/team-agent/src/lifecycle/tests/lifecycle_rollback_red.rs +651 -0
  135. package/crates/team-agent/src/lifecycle/tests/main_preserved.rs +2 -0
  136. package/crates/team-agent/src/lifecycle/tests/quick_start_worker_readiness_red.rs +332 -0
  137. package/crates/team-agent/src/lifecycle/tests/realmachine_clusters_1_6_red.rs +846 -0
  138. package/crates/team-agent/src/lifecycle/tests/realmachine_residual_g1_g4_red.rs +648 -0
  139. package/crates/team-agent/src/lifecycle/tests/restart_build_before_destroy_0540_contract.rs +910 -0
  140. package/crates/team-agent/src/lifecycle/tests/restart_liveness_red.rs +630 -0
  141. package/crates/team-agent/src/lifecycle/tests/restart_rebind_hotfix_252_red.rs +1083 -0
  142. package/crates/team-agent/src/lifecycle/tests/restart_session_capture_red.rs +1677 -0
  143. package/crates/team-agent/src/lifecycle/tests/resume_recover_red.rs +410 -0
  144. package/crates/team-agent/src/lifecycle/tests/stale_team_saveconflict_contract.rs +428 -0
  145. package/crates/team-agent/src/lifecycle/tests/startup_latency_contract.rs +1032 -0
  146. package/crates/team-agent/src/lifecycle/tests/status_credential_redaction_contract.rs +869 -0
  147. package/crates/team-agent/src/lifecycle/tests/subprep_codex_trust_roundtrip_red.rs +1249 -0
  148. package/crates/team-agent/src/lifecycle/tests/swallow_batch4_semantics_red.rs +342 -0
  149. package/crates/team-agent/src/lifecycle/tests/team_in_team_identity_scope_red.rs +542 -0
  150. package/crates/team-agent/src/lifecycle/tests/team_in_team_sibling_quick_start_red.rs +1103 -0
  151. package/crates/team-agent/src/lifecycle/tests/team_in_team_state_scope_red.rs +1276 -0
  152. package/crates/team-agent/src/lifecycle/tests/team_key_retirement_roster_red.rs +145 -0
  153. package/crates/team-agent/src/lifecycle/tests/team_key_retirement_txn_red.rs +603 -0
  154. package/crates/team-agent/src/lifecycle/tests/test_isolation_escape_contract.rs +837 -0
  155. package/crates/team-agent/src/lifecycle/tests/upgrade_compat_0211_red.rs +928 -0
  156. package/crates/team-agent/src/lifecycle/tests/verify_rs031_window_consistency_red.rs +951 -0
  157. package/crates/team-agent/src/lifecycle/tests/worker_dangerous_bypass_argv_red.rs +327 -0
  158. package/crates/team-agent/src/lifecycle/tests/worker_spawn_env_red.rs +760 -0
  159. package/crates/team-agent/src/lifecycle/tests.rs +59 -0
  160. package/crates/team-agent/src/lifecycle/types.rs +0 -9
  161. package/crates/team-agent/src/lifecycle/worker_command_context.rs +29 -11
  162. package/crates/team-agent/src/mcp_server/helpers.rs +1 -0
  163. package/crates/team-agent/src/mcp_server/lifecycle_tools/mod.rs +1 -0
  164. package/crates/team-agent/src/mcp_server/lifecycle_tools/state_status.rs +1 -0
  165. package/crates/team-agent/src/mcp_server/mod.rs +1 -0
  166. package/crates/team-agent/src/mcp_server/normalize.rs +5 -3
  167. package/crates/team-agent/src/mcp_server/tests/send.rs +26 -0
  168. package/crates/team-agent/src/mcp_server/tests/wire.rs +1 -1
  169. package/crates/team-agent/src/mcp_server/tools.rs +101 -66
  170. package/crates/team-agent/src/mcp_server/types.rs +6 -18
  171. package/crates/team-agent/src/mcp_server/wire.rs +32 -26
  172. package/crates/team-agent/src/messaging/activity.rs +1 -0
  173. package/crates/team-agent/src/messaging/address.rs +1 -0
  174. package/crates/team-agent/src/messaging/delivery.rs +109 -41
  175. package/crates/team-agent/src/messaging/helpers.rs +2 -1
  176. package/crates/team-agent/src/messaging/leader_channel.rs +34 -10
  177. package/crates/team-agent/src/messaging/leader_receiver.rs +42 -24
  178. package/crates/team-agent/src/messaging/mod.rs +10 -14
  179. package/crates/team-agent/src/messaging/peers.rs +2 -0
  180. package/crates/team-agent/src/messaging/persist.rs +3 -1
  181. package/crates/team-agent/src/messaging/presentation.rs +111 -1
  182. package/crates/team-agent/src/messaging/results.rs +166 -17
  183. package/crates/team-agent/src/messaging/scheduler.rs +1 -0
  184. package/crates/team-agent/src/messaging/selftest.rs +1 -1
  185. package/crates/team-agent/src/messaging/send.rs +135 -89
  186. package/crates/team-agent/src/messaging/tests/leader_channel.rs +6 -6
  187. package/crates/team-agent/src/messaging/tests/leader_inject_acceptance.rs +187 -0
  188. package/crates/team-agent/src/messaging/tests/mod.rs +5 -0
  189. package/crates/team-agent/src/messaging/tests/runtime.rs +13 -6
  190. package/crates/team-agent/src/messaging/types.rs +7 -2
  191. package/crates/team-agent/src/messaging/watchers.rs +24 -13
  192. package/crates/team-agent/src/model/enums.rs +1 -0
  193. package/crates/team-agent/src/model/errors.rs +1 -0
  194. package/crates/team-agent/src/model/ids.rs +1 -0
  195. package/crates/team-agent/src/model/mod.rs +2 -0
  196. package/crates/team-agent/src/model/name_similarity.rs +1 -0
  197. package/crates/team-agent/src/model/pane_authority_refusal.rs +359 -0
  198. package/crates/team-agent/src/model/paths.rs +1 -0
  199. package/crates/team-agent/src/model/permissions.rs +3 -2
  200. package/crates/team-agent/src/model/routing.rs +1 -0
  201. package/crates/team-agent/src/model/spec.rs +17 -0
  202. package/crates/team-agent/src/model/task_graph.rs +1 -0
  203. package/crates/team-agent/src/model/yaml/tests.rs +1 -0
  204. package/crates/team-agent/src/model/yaml.rs +1 -0
  205. package/crates/team-agent/src/packaging/install.rs +9 -147
  206. package/crates/team-agent/src/packaging/migrate.rs +2 -0
  207. package/crates/team-agent/src/packaging/mod.rs +1 -0
  208. package/crates/team-agent/src/packaging/repair.rs +2 -0
  209. package/crates/team-agent/src/packaging/tests.rs +29 -218
  210. package/crates/team-agent/src/packaging/types.rs +8 -15
  211. package/crates/team-agent/src/platform/argv.rs +4 -0
  212. package/crates/team-agent/src/platform/errors.rs +10 -1
  213. package/crates/team-agent/src/platform/file_lock.rs +4 -157
  214. package/crates/team-agent/src/platform/mod.rs +0 -61
  215. package/crates/team-agent/src/platform/process.rs +1 -0
  216. package/crates/team-agent/src/provider/adapter.rs +1 -0
  217. package/crates/team-agent/src/provider/adapters/claude.rs +1 -0
  218. package/crates/team-agent/src/provider/adapters/claude_fork.rs +1 -0
  219. package/crates/team-agent/src/provider/adapters/codex.rs +1 -0
  220. package/crates/team-agent/src/provider/adapters/copilot.rs +1 -0
  221. package/crates/team-agent/src/provider/adapters/copilot_fork.rs +1 -0
  222. package/crates/team-agent/src/provider/adapters/fake.rs +1 -0
  223. package/crates/team-agent/src/provider/adapters/mod.rs +1 -0
  224. package/crates/team-agent/src/provider/approvals/mod.rs +1 -0
  225. package/crates/team-agent/src/provider/approvals/parsing.rs +2 -5
  226. package/crates/team-agent/src/provider/approvals/runtime_prompts.rs +6 -5
  227. package/crates/team-agent/src/provider/classify.rs +1 -0
  228. package/crates/team-agent/src/provider/faults.rs +1 -26
  229. package/crates/team-agent/src/provider/helpers.rs +1 -0
  230. package/crates/team-agent/src/provider/mod.rs +1 -1
  231. package/crates/team-agent/src/provider/session/capture.rs +41 -22
  232. package/crates/team-agent/src/provider/session/context_fork/claude.rs +1 -0
  233. package/crates/team-agent/src/provider/session/context_fork/codex.rs +1 -0
  234. package/crates/team-agent/src/provider/session/context_fork/outcome.rs +1 -0
  235. package/crates/team-agent/src/provider/session/context_fork.rs +1 -0
  236. package/crates/team-agent/src/provider/session/mod.rs +2 -4
  237. package/crates/team-agent/src/provider/session/resume.rs +2 -209
  238. package/crates/team-agent/src/provider/session_scan/claude.rs +93 -3
  239. package/crates/team-agent/src/provider/session_scan/codex.rs +1 -0
  240. package/crates/team-agent/src/provider/session_scan/common.rs +24 -7
  241. package/crates/team-agent/src/provider/session_scan/copilot.rs +1 -0
  242. package/crates/team-agent/src/provider/session_scan.rs +6 -42
  243. package/crates/team-agent/src/provider/startup_prompt.rs +1 -0
  244. package/crates/team-agent/src/provider/types.rs +1 -0
  245. package/crates/team-agent/src/provider/wire.rs +1 -0
  246. package/crates/team-agent/src/state/identity.rs +16 -5
  247. package/crates/team-agent/src/state/identity_keys.rs +1 -0
  248. package/crates/team-agent/src/state/mod.rs +2 -0
  249. package/crates/team-agent/src/state/owner_gate.rs +5 -0
  250. package/crates/team-agent/src/state/ownership.rs +10 -4
  251. package/crates/team-agent/src/state/paths.rs +12 -0
  252. package/crates/team-agent/src/state/persist.rs +9 -3
  253. package/crates/team-agent/src/state/projection.rs +15 -2
  254. package/crates/team-agent/src/state/repository/intent.rs +1 -0
  255. package/crates/team-agent/src/state/repository/tests.rs +1 -0
  256. package/crates/team-agent/src/state/repository.rs +7 -0
  257. package/crates/team-agent/src/state/selector.rs +1 -0
  258. package/crates/team-agent/src/tmux_backend/tests.rs +39 -0
  259. package/crates/team-agent/src/tmux_backend.rs +63 -0
  260. package/crates/team-agent/src/topology.rs +3 -0
  261. package/crates/team-agent/src/transport.rs +15 -0
  262. package/package.json +4 -4
  263. package/skills/team-agent/command-coverage.json +379 -0
  264. package/crates/team-agent/src/leader/inject.rs +0 -33
  265. package/crates/team-agent/src/provider/command.rs +0 -80
@@ -1,4 +1,5 @@
1
- //! unit-5 (Stage 2) — resume preflight + closed `ResumeRefusalReason` enum.
1
+ //!
2
+ //! unit-5 (Stage 2) — closed `ResumeRefusalReason` enum and recovery hints.
2
3
  //!
3
4
  //! Today the restart resume gate flattens every refusal into one of two
4
5
  //! opaque strings:
@@ -159,110 +160,6 @@ impl ResumeRefusalReason {
159
160
  }
160
161
  }
161
162
 
162
- /// Outcome of `ResumePreflight::check`.
163
- #[derive(Debug, Clone, PartialEq, Eq)]
164
- pub enum ResumePreflightOutcome {
165
- /// Worker can resume from the named session.
166
- Resume { session_id: String },
167
- /// `--allow-fresh` was set; worker will start fresh.
168
- FreshStart,
169
- /// Resume refused. Caller MUST NOT proceed with teardown/spawn.
170
- Refuse { reason: ResumeRefusalReason },
171
- }
172
-
173
- /// Information about whether a provider backing file is present on disk.
174
- #[derive(Debug, Clone, PartialEq, Eq)]
175
- pub struct ProviderBackingCheck {
176
- pub paths: Vec<PathBuf>,
177
- pub exists: bool,
178
- }
179
-
180
- /// Per-decision detail emitted to events / JSON.
181
- #[derive(Debug, Clone, PartialEq, Eq)]
182
- pub struct ResumeDecisionDetail {
183
- pub agent_id: String,
184
- pub session_id: Option<String>,
185
- pub provider: String,
186
- pub backing: Option<ProviderBackingCheck>,
187
- pub outcome: ResumePreflightOutcome,
188
- }
189
-
190
- /// Pure resume preflight evaluator. No I/O — callers pass facts in.
191
- pub struct ResumePreflight;
192
-
193
- impl ResumePreflight {
194
- /// Decide a single worker's resume outcome from the facts the
195
- /// restart code already collected. Mirrors the existing
196
- /// `classify_restart_plan_with_resume_validation` decision tree:
197
- ///
198
- /// * session_id present + provider can resume + backing exists -> Resume
199
- /// * session_id present + (no resume OR no backing) + allow_fresh -> FreshStart
200
- /// * session_id present + (no resume OR no backing) + !allow_fresh -> Refuse
201
- /// * session_id absent + allow_fresh -> FreshStart
202
- /// * session_id absent + !allow_fresh -> Refuse(NoSessionId)
203
- pub fn check(
204
- session_id: Option<&str>,
205
- provider_can_resume: bool,
206
- backing: Option<&ProviderBackingCheck>,
207
- provider_name: &str,
208
- allow_fresh: bool,
209
- ) -> ResumePreflightOutcome {
210
- Self::check_with_hint(
211
- session_id,
212
- provider_can_resume,
213
- backing,
214
- provider_name,
215
- allow_fresh,
216
- None,
217
- )
218
- }
219
-
220
- /// Layer 2 self-healing variant: same decision tree as
221
- /// [`Self::check`], but propagates an optional `RecoveryHint` into
222
- /// the `SessionBackingStoreMissing` refusal so the CLI / event log
223
- /// can surface a "look for session named X under cwd Y" pointer to
224
- /// the operator. The hint is for HUMAN consumption only — no
225
- /// auto-resume happens off it (Layer 3 follow-up).
226
- pub fn check_with_hint(
227
- session_id: Option<&str>,
228
- provider_can_resume: bool,
229
- backing: Option<&ProviderBackingCheck>,
230
- provider_name: &str,
231
- allow_fresh: bool,
232
- recovery_hint: Option<RecoveryHint>,
233
- ) -> ResumePreflightOutcome {
234
- match session_id {
235
- Some(sid) if provider_can_resume => {
236
- let backing_present = backing.map(|b| b.exists).unwrap_or(true);
237
- if backing_present {
238
- ResumePreflightOutcome::Resume {
239
- session_id: sid.to_string(),
240
- }
241
- } else if allow_fresh {
242
- ResumePreflightOutcome::FreshStart
243
- } else {
244
- ResumePreflightOutcome::Refuse {
245
- reason: ResumeRefusalReason::SessionBackingStoreMissing {
246
- checked_paths: backing.map(|b| b.paths.clone()).unwrap_or_default(),
247
- recovery_hint,
248
- },
249
- }
250
- }
251
- }
252
- Some(_) if allow_fresh => ResumePreflightOutcome::FreshStart,
253
- Some(_) => ResumePreflightOutcome::Refuse {
254
- reason: ResumeRefusalReason::ProviderResumeUnsupported {
255
- provider: provider_name.to_string(),
256
- },
257
- },
258
- None if allow_fresh => ResumePreflightOutcome::FreshStart,
259
- None => ResumePreflightOutcome::Refuse {
260
- reason: ResumeRefusalReason::NoSessionId,
261
- },
262
- }
263
- }
264
- }
265
-
266
163
  #[cfg(test)]
267
164
  mod tests {
268
165
  use super::*;
@@ -287,58 +184,6 @@ mod tests {
287
184
  assert!(matches!(r, ResumeRefusalReason::Other { .. }));
288
185
  }
289
186
 
290
- #[test]
291
- fn preflight_resume_when_session_and_backing_present() {
292
- let backing = ProviderBackingCheck {
293
- paths: vec![PathBuf::from("/tmp/x.jsonl")],
294
- exists: true,
295
- };
296
- let out = ResumePreflight::check(Some("sess-1"), true, Some(&backing), "codex", false);
297
- assert!(matches!(out, ResumePreflightOutcome::Resume { .. }));
298
- }
299
-
300
- #[test]
301
- fn preflight_refuses_when_session_id_missing_without_allow_fresh() {
302
- let out = ResumePreflight::check(None, true, None, "codex", false);
303
- assert_eq!(
304
- out,
305
- ResumePreflightOutcome::Refuse {
306
- reason: ResumeRefusalReason::NoSessionId
307
- }
308
- );
309
- }
310
-
311
- #[test]
312
- fn preflight_distinguishes_backing_missing_from_no_session_id() {
313
- let backing = ProviderBackingCheck {
314
- paths: vec![PathBuf::from("/missing.jsonl")],
315
- exists: false,
316
- };
317
- let out = ResumePreflight::check(Some("sess-x"), true, Some(&backing), "codex", false);
318
- match out {
319
- ResumePreflightOutcome::Refuse { reason } => {
320
- assert_eq!(reason.wire(), "session_backing_store_missing");
321
- }
322
- other => panic!("expected backing-missing refusal; got {other:?}"),
323
- }
324
- }
325
-
326
- #[test]
327
- fn preflight_allow_fresh_converts_refusals_to_fresh() {
328
- let backing = ProviderBackingCheck {
329
- paths: vec![PathBuf::from("/missing.jsonl")],
330
- exists: false,
331
- };
332
- assert_eq!(
333
- ResumePreflight::check(None, true, None, "codex", true),
334
- ResumePreflightOutcome::FreshStart
335
- );
336
- assert_eq!(
337
- ResumePreflight::check(Some("sess-x"), true, Some(&backing), "codex", true),
338
- ResumePreflightOutcome::FreshStart
339
- );
340
- }
341
-
342
187
  // Layer 2 self-healing tests ────────────────────────────────────────────
343
188
 
344
189
  #[test]
@@ -364,56 +209,4 @@ mod tests {
364
209
  };
365
210
  assert_eq!(hint.picker_hint(), "codex session");
366
211
  }
367
-
368
- #[test]
369
- fn preflight_with_hint_attaches_to_backing_missing_refusal() {
370
- let backing = ProviderBackingCheck {
371
- paths: vec![PathBuf::from("/missing.jsonl")],
372
- exists: false,
373
- };
374
- let hint = RecoveryHint {
375
- provider_session_name_hint: Some("coder".to_string()),
376
- spawn_cwd: Some(PathBuf::from("/repo")),
377
- provider: "claude".to_string(),
378
- };
379
- let out = ResumePreflight::check_with_hint(
380
- Some("sess-1"),
381
- true,
382
- Some(&backing),
383
- "claude",
384
- false,
385
- Some(hint.clone()),
386
- );
387
- match out {
388
- ResumePreflightOutcome::Refuse {
389
- reason:
390
- ResumeRefusalReason::SessionBackingStoreMissing {
391
- recovery_hint: Some(h),
392
- ..
393
- },
394
- } => {
395
- assert_eq!(h, hint);
396
- }
397
- other => panic!("expected SessionBackingStoreMissing with hint; got {other:?}"),
398
- }
399
- }
400
-
401
- #[test]
402
- fn preflight_no_hint_legacy_behavior_unchanged() {
403
- // Default check() (no hint) still produces None recovery_hint —
404
- // wire-string-compatible with pre-Layer-2 callers.
405
- let backing = ProviderBackingCheck {
406
- paths: vec![PathBuf::from("/missing.jsonl")],
407
- exists: false,
408
- };
409
- let out = ResumePreflight::check(Some("sess-x"), true, Some(&backing), "codex", false);
410
- match out {
411
- ResumePreflightOutcome::Refuse {
412
- reason: ResumeRefusalReason::SessionBackingStoreMissing { recovery_hint, .. },
413
- } => {
414
- assert!(recovery_hint.is_none());
415
- }
416
- other => panic!("expected backing-missing refusal; got {other:?}"),
417
- }
418
- }
419
212
  }
@@ -1,6 +1,8 @@
1
+ //!
1
2
  use std::path::{Path, PathBuf};
2
3
 
3
- use crate::provider::types::SessionId;
4
+ use crate::provider::helpers::find_session_id;
5
+ use crate::provider::types::{CaptureVia, CapturedSession, Confidence, RolloutPath, SessionId};
4
6
  use crate::provider::Provider;
5
7
 
6
8
  use super::{CaptureSessionContext, CapturedSessionCandidate};
@@ -14,7 +16,7 @@ pub(crate) fn projects_dir_for_cwd(home: &Path, spawn_cwd: &Path) -> Option<Path
14
16
  Some(home.join(".claude").join("projects").join(encoded))
15
17
  }
16
18
 
17
- fn encode_projects_dir(path: &str) -> String {
19
+ pub(super) fn encode_projects_dir(path: &str) -> String {
18
20
  let mut out = String::with_capacity(path.len());
19
21
  for c in path.chars() {
20
22
  if c.is_ascii_alphanumeric() {
@@ -26,6 +28,65 @@ fn encode_projects_dir(path: &str) -> String {
26
28
  out
27
29
  }
28
30
 
31
+ pub(super) fn scan_expected_session(
32
+ context: &CaptureSessionContext,
33
+ ) -> Vec<CapturedSessionCandidate> {
34
+ let Some(expected) = context.expected_session_id.as_ref() else {
35
+ return Vec::new();
36
+ };
37
+ let Some(projects_root) = context.provider_projects_root.clone().or_else(|| {
38
+ std::env::var_os("HOME")
39
+ .map(PathBuf::from)
40
+ .map(|home| home.join(".claude").join("projects"))
41
+ }) else {
42
+ return Vec::new();
43
+ };
44
+ let canonical =
45
+ std::fs::canonicalize(&context.spawn_cwd).unwrap_or_else(|_| context.spawn_cwd.clone());
46
+ let encoded = encode_projects_dir(&canonical.to_string_lossy());
47
+ if encoded.is_empty() {
48
+ return Vec::new();
49
+ }
50
+ let path = projects_root
51
+ .join(encoded)
52
+ .join(format!("{}.jsonl", expected.as_str()));
53
+ let Ok(text) = super::common::read_head_text(&path, super::common::CAPTURE_HEAD_BYTES) else {
54
+ return Vec::new();
55
+ };
56
+ let records = super::common::parse_session_records(&text);
57
+ let session_matches = records
58
+ .iter()
59
+ .find_map(find_session_id)
60
+ .is_some_and(|session_id| session_id == expected.as_str());
61
+ let has_lifecycle_record = records.iter().any(|record| {
62
+ matches!(
63
+ record.get("type").and_then(serde_json::Value::as_str),
64
+ Some("user" | "assistant")
65
+ )
66
+ });
67
+ if !session_matches
68
+ || !has_lifecycle_record
69
+ || records_have_leader_marker(&records)
70
+ || !records.iter().any(has_cwd_field)
71
+ {
72
+ return Vec::new();
73
+ }
74
+ let embedded_agent_id = super::common::embedded_team_agent_worker_id_from_text(&text);
75
+ let positive_agent_id_match = embedded_agent_id.as_deref() == Some(context.agent_id.as_str());
76
+ vec![CapturedSessionCandidate {
77
+ captured: CapturedSession {
78
+ session_id: Some(expected.clone()),
79
+ rollout_path: Some(RolloutPath::new(path)),
80
+ captured_via: CaptureVia::FsWatch,
81
+ attribution_confidence: Confidence::High,
82
+ spawn_cwd: context.spawn_cwd.clone(),
83
+ },
84
+ embedded_agent_id,
85
+ positive_agent_id_match,
86
+ agent_path_match: false,
87
+ }]
88
+ }
89
+
29
90
  pub(crate) fn rollout_path_has_leader_marker(provider: Provider, rollout_path: &Path) -> bool {
30
91
  if !matches!(provider, Provider::Claude | Provider::ClaudeCode) {
31
92
  return false;
@@ -112,6 +173,7 @@ mod tests {
112
173
  std::fs::create_dir_all(dir).unwrap();
113
174
  let path = dir.join(format!("{uuid}.jsonl"));
114
175
  let line = serde_json::json!({
176
+ "type": "user",
115
177
  "sessionId": uuid,
116
178
  "cwd": cwd.to_string_lossy(),
117
179
  });
@@ -119,6 +181,11 @@ mod tests {
119
181
  path
120
182
  }
121
183
 
184
+ fn expected_transcript_dir(projects_root: &Path, cwd: &Path) -> PathBuf {
185
+ let canonical = std::fs::canonicalize(cwd).unwrap_or_else(|_| cwd.to_path_buf());
186
+ projects_root.join(encode_projects_dir(&canonical.to_string_lossy()))
187
+ }
188
+
122
189
  #[test]
123
190
  fn claude_leader_marker_in_custom_title_is_detected() {
124
191
  let records = vec![serde_json::json!({
@@ -205,7 +272,11 @@ mod tests {
205
272
  std::fs::create_dir_all(&cwd).unwrap();
206
273
  let proj = base.join("projects");
207
274
  write_transcript(&proj, "11111111-1111-4111-8111-111111111111", &cwd);
208
- write_transcript(&proj, "22222222-2222-4222-8222-222222222222", &cwd);
275
+ let expected = write_transcript(
276
+ &expected_transcript_dir(&proj, &cwd),
277
+ "22222222-2222-4222-8222-222222222222",
278
+ &cwd,
279
+ );
209
280
  let ctx = CaptureSessionContext {
210
281
  agent_id: "w1".to_string(),
211
282
  spawn_cwd: cwd.clone(),
@@ -221,6 +292,10 @@ mod tests {
221
292
  out[0].captured.session_id.as_ref().unwrap().as_str(),
222
293
  "22222222-2222-4222-8222-222222222222"
223
294
  );
295
+ assert_eq!(
296
+ out[0].captured.rollout_path.as_ref().unwrap().as_path(),
297
+ expected
298
+ );
224
299
  let _ = std::fs::remove_dir_all(&base);
225
300
  }
226
301
 
@@ -284,6 +359,21 @@ mod tests {
284
359
  let proj = base.join("projects");
285
360
  let leader = write_transcript(&proj, "11111111-1111-4111-8111-111111111111", &cwd);
286
361
  let stale = write_transcript(&proj, "22222222-2222-4222-8222-222222222222", &cwd);
362
+ let addressed =
363
+ expected_transcript_dir(&proj, &cwd).join("99999999-9999-4999-8999-999999999999.jsonl");
364
+ std::fs::create_dir_all(addressed.parent().unwrap()).unwrap();
365
+ std::fs::write(
366
+ &addressed,
367
+ format!(
368
+ "{}\n",
369
+ serde_json::json!({
370
+ "type": "user",
371
+ "sessionId": "22222222-2222-4222-8222-222222222222",
372
+ "cwd": cwd.to_string_lossy(),
373
+ })
374
+ ),
375
+ )
376
+ .unwrap();
287
377
  let _ = (leader, stale);
288
378
  let ctx = CaptureSessionContext {
289
379
  agent_id: "claude-worker".to_string(),
@@ -1,3 +1,4 @@
1
+ //!
1
2
  pub(super) fn parse_spawned_at(raw: &str) -> Option<std::time::SystemTime> {
2
3
  chrono::DateTime::parse_from_rfc3339(raw)
3
4
  .ok()
@@ -1,3 +1,4 @@
1
+ //!
1
2
  use std::path::{Path, PathBuf};
2
3
 
3
4
  use crate::provider::helpers::{find_session_id, parse_jsonl_records};
@@ -26,25 +27,36 @@ pub(super) fn candidate_session_files(
26
27
  ) -> Result<Vec<SessionCandidate>, ProviderError> {
27
28
  let mut out = Vec::new();
28
29
  if let Some(root) = context.provider_projects_root.as_ref() {
29
- collect_optional_candidate_files(root, &context.agent_id, &mut out)?;
30
+ let allowed_team_root =
31
+ matches!(provider, Provider::Claude | Provider::ClaudeCode).then_some(root.as_path());
32
+ collect_optional_candidate_files(root, &context.agent_id, allowed_team_root, &mut out)?;
30
33
  }
31
- collect_candidate_files(&context.spawn_cwd, &context.agent_id, 0, false, &mut out)?;
34
+ collect_candidate_files(
35
+ &context.spawn_cwd,
36
+ &context.agent_id,
37
+ 0,
38
+ false,
39
+ None,
40
+ &mut out,
41
+ )?;
32
42
  if let Some(home) = std::env::var_os("HOME").map(PathBuf::from) {
33
43
  match provider {
34
44
  Provider::Codex => {
35
45
  collect_optional_candidate_files(
36
46
  &home.join(".codex").join("sessions"),
37
47
  &context.agent_id,
48
+ None,
38
49
  &mut out,
39
50
  )?;
40
51
  }
41
52
  Provider::Claude | Provider::ClaudeCode => {
42
53
  if let Some(dir) = super::claude::projects_dir_for_cwd(&home, &context.spawn_cwd) {
43
- collect_optional_candidate_files(&dir, &context.agent_id, &mut out)?;
54
+ collect_optional_candidate_files(&dir, &context.agent_id, None, &mut out)?;
44
55
  }
45
56
  collect_optional_candidate_files(
46
57
  &home.join(".claude").join("projects"),
47
58
  &context.agent_id,
59
+ None,
48
60
  &mut out,
49
61
  )?;
50
62
  }
@@ -275,10 +287,11 @@ fn complete_head_text(bytes: &[u8]) -> String {
275
287
  fn collect_optional_candidate_files(
276
288
  dir: &Path,
277
289
  agent_id: &str,
290
+ allowed_team_root: Option<&Path>,
278
291
  out: &mut Vec<SessionCandidate>,
279
292
  ) -> Result<(), ProviderError> {
280
293
  if dir.exists() {
281
- let _ = collect_candidate_files(dir, agent_id, 0, true, out);
294
+ let _ = collect_candidate_files(dir, agent_id, 0, true, allowed_team_root, out);
282
295
  }
283
296
  Ok(())
284
297
  }
@@ -288,6 +301,7 @@ fn collect_candidate_files(
288
301
  agent_id: &str,
289
302
  depth: usize,
290
303
  requires_cwd_match: bool,
304
+ allowed_team_root: Option<&Path>,
291
305
  out: &mut Vec<SessionCandidate>,
292
306
  ) -> Result<(), ProviderError> {
293
307
  if depth > 4 {
@@ -309,9 +323,10 @@ fn collect_candidate_files(
309
323
  agent_id,
310
324
  depth.saturating_add(1),
311
325
  requires_cwd_match,
326
+ allowed_team_root,
312
327
  out,
313
328
  )?;
314
- } else if looks_like_session_file(&path, agent_id) {
329
+ } else if looks_like_session_file(&path, agent_id, allowed_team_root) {
315
330
  out.push(SessionCandidate {
316
331
  path,
317
332
  requires_cwd_match,
@@ -321,8 +336,10 @@ fn collect_candidate_files(
321
336
  Ok(())
322
337
  }
323
338
 
324
- fn looks_like_session_file(path: &Path, agent_id: &str) -> bool {
325
- if path_is_under_team_runtime(path) {
339
+ fn looks_like_session_file(path: &Path, agent_id: &str, allowed_team_root: Option<&Path>) -> bool {
340
+ if path_is_under_team_runtime(path)
341
+ && !allowed_team_root.is_some_and(|root| path.starts_with(root))
342
+ {
326
343
  return false;
327
344
  }
328
345
  let Some(name) = path.file_name().and_then(|s| s.to_str()) else {
@@ -1,3 +1,4 @@
1
+ //!
1
2
  use std::path::Path;
2
3
 
3
4
  use crate::provider::types::{CaptureVia, CapturedSession, Confidence, RolloutPath, SessionId};
@@ -1,3 +1,4 @@
1
+ //!
1
2
  //! Provider session-scan boundary.
2
3
  //!
3
4
  //! `ProviderAdapter` keeps the public polling facade in `adapter.rs`; this
@@ -14,27 +15,6 @@ pub(crate) mod codex;
14
15
  pub(crate) mod common;
15
16
  pub(crate) mod copilot;
16
17
 
17
- /// Marker label for which scan strategy a provider uses.
18
- #[derive(Debug, Clone, Copy, PartialEq, Eq)]
19
- pub enum SessionScanStrategy {
20
- CodexJsonl,
21
- ClaudeTranscript,
22
- CopilotHome,
23
- NotRequired,
24
- }
25
-
26
- impl SessionScanStrategy {
27
- pub fn for_provider(provider: Provider) -> Self {
28
- match provider {
29
- Provider::Codex => Self::CodexJsonl,
30
- Provider::Claude | Provider::ClaudeCode => Self::ClaudeTranscript,
31
- Provider::Copilot => Self::CopilotHome,
32
- Provider::GeminiCli => Self::NotRequired,
33
- Provider::Fake => Self::NotRequired,
34
- }
35
- }
36
- }
37
-
38
18
  #[derive(Debug, Clone, PartialEq, Eq)]
39
19
  pub struct CaptureSessionContext {
40
20
  pub agent_id: String,
@@ -61,6 +41,11 @@ pub(crate) fn scan_session_candidates_once(
61
41
  if matches!(provider, Provider::Copilot) {
62
42
  return Ok(copilot::scan_session_store(context));
63
43
  }
44
+ if matches!(provider, Provider::Claude | Provider::ClaudeCode)
45
+ && context.expected_session_id.is_some()
46
+ {
47
+ return Ok(claude::scan_expected_session(context));
48
+ }
64
49
 
65
50
  let candidates = common::candidate_session_files(provider, context)?;
66
51
  let mut out = common::parse_candidate_files(provider, context, candidates);
@@ -82,24 +67,3 @@ pub(crate) fn scan_session_candidates_once(
82
67
  }
83
68
 
84
69
  pub(crate) use claude::rollout_path_has_leader_marker as rollout_path_has_claude_leader_marker;
85
-
86
- #[cfg(test)]
87
- mod tests {
88
- use super::*;
89
-
90
- #[test]
91
- fn strategy_per_provider() {
92
- assert_eq!(
93
- SessionScanStrategy::for_provider(Provider::Codex),
94
- SessionScanStrategy::CodexJsonl
95
- );
96
- assert_eq!(
97
- SessionScanStrategy::for_provider(Provider::Claude),
98
- SessionScanStrategy::ClaudeTranscript
99
- );
100
- assert_eq!(
101
- SessionScanStrategy::for_provider(Provider::Fake),
102
- SessionScanStrategy::NotRequired
103
- );
104
- }
105
- }
@@ -1,3 +1,4 @@
1
+ //!
1
2
  //! codex startup-prompt recognizer — workspace-trust + update-skip screen detection.
2
3
  //!
3
4
  //! Golden (READ-ONLY truth `team-agent-public` v0.2.11): `provider_cli/codex.py`
@@ -1,3 +1,4 @@
1
+ //!
1
2
  //! provider 共享类型:enums / newtypes / ProviderCaps / ProviderError / 捕获+classify payload / 占位结构。
2
3
 
3
4
  use std::collections::{BTreeMap, BTreeSet};
@@ -1,3 +1,4 @@
1
+ //!
1
2
  //! Provider wire-format helpers — the single source of truth for converting
2
3
  //! `Provider` ↔ string forms used across state JSON, CLI args, log fields,
3
4
  //! and the workers we shell out to.
@@ -1,3 +1,4 @@
1
+ //!
1
2
  //! identity:leader receiver identity 派生 / 迁移 / first-time binding(真相源 `state.py`)。
2
3
  //!
3
4
  //! Python 直接读 `os.environ`/`os.getcwd()`/`Path.resolve()`;此处把**环境变量**经
@@ -44,8 +45,9 @@ fn path_str(p: &Path) -> String {
44
45
  p.to_string_lossy().into_owned()
45
46
  }
46
47
 
48
+ ///
47
49
  /// `_identity_os_user`(`state.py:275`):`USER` or `USERNAME` or `""`。
48
- pub fn identity_os_user(env: &dyn IdentityEnv) -> String {
50
+ fn identity_os_user(env: &dyn IdentityEnv) -> String {
49
51
  let user = env_str(env, "USER");
50
52
  if !user.is_empty() {
51
53
  return user;
@@ -57,9 +59,10 @@ pub fn identity_os_user(env: &dyn IdentityEnv) -> String {
57
59
  String::new()
58
60
  }
59
61
 
62
+ ///
60
63
  /// `_identity_machine_fingerprint`(`state.py:279`):team_owner/leader_receiver 的非空
61
64
  /// machine_fingerprint 优先,否则 env `TEAM_AGENT_MACHINE_FINGERPRINT` or `""`。
62
- pub fn identity_machine_fingerprint(state: &Value, env: &dyn IdentityEnv) -> String {
65
+ fn identity_machine_fingerprint(state: &Value, env: &dyn IdentityEnv) -> String {
63
66
  for key in ["team_owner", "leader_receiver"] {
64
67
  if let Some(fp) = state
65
68
  .get(key)
@@ -74,9 +77,10 @@ pub fn identity_machine_fingerprint(state: &Value, env: &dyn IdentityEnv) -> Str
74
77
  env_str(env, "TEAM_AGENT_MACHINE_FINGERPRINT")
75
78
  }
76
79
 
80
+ ///
77
81
  /// `_identity_workspace_abspath`(`state.py:264`):按 workspace→team_dir→spec_path→fallback
78
82
  /// 顺序派生 workspace 绝对路径(各分支先 `resolve()` 再做 parent 导航)。
79
- pub fn identity_workspace_abspath(
83
+ fn identity_workspace_abspath(
80
84
  state: &Value,
81
85
  env: &dyn IdentityEnv,
82
86
  workspace: Option<&Path>,
@@ -157,9 +161,10 @@ fn leader_session_uuid_for_state(
157
161
  Ok(uuid.as_str().to_string())
158
162
  }
159
163
 
164
+ ///
160
165
  /// `_migrate_team_identity`(`state.py:295`):为缺 `leader_session_uuid` 的 team_owner /
161
166
  /// leader_receiver 填入派生 uuid。返回是否有改动。
162
- pub fn migrate_team_identity(
167
+ fn migrate_team_identity(
163
168
  state: &mut Value,
164
169
  env: &dyn IdentityEnv,
165
170
  workspace: Option<&Path>,
@@ -189,6 +194,7 @@ pub fn migrate_team_identity(
189
194
  Ok(changed)
190
195
  }
191
196
 
197
+ ///
192
198
  /// `_migrate_state_identity`(`state.py:306`):顶层(有 session_name 时)+ 每个 team 子状态迁移。
193
199
  pub fn migrate_state_identity(
194
200
  state: &mut Value,
@@ -220,6 +226,7 @@ pub fn migrate_state_identity(
220
226
  Ok(changed)
221
227
  }
222
228
 
229
+ ///
223
230
  /// `_caller_identity_from_env`(`state.py:316`):env override/env/derived → 5 字段 caller 身份。
224
231
  pub fn caller_identity_from_env(
225
232
  state: Option<&Value>,
@@ -282,9 +289,10 @@ pub fn caller_identity_from_env(
282
289
  })
283
290
  }
284
291
 
292
+ ///
285
293
  /// `populate_team_owner_from_env`(`state.py:414`):已有 team_owner → 仅补迁移 uuid 后返回;
286
294
  /// 否则从 env caller 身份 seed team_owner(无 pane_id → `None`)。`now_iso` = claimed_at。
287
- pub fn populate_team_owner_from_env(
295
+ fn populate_team_owner_from_env(
288
296
  state: &mut Value,
289
297
  source: &str,
290
298
  env: &dyn IdentityEnv,
@@ -320,6 +328,7 @@ pub fn populate_team_owner_from_env(
320
328
  Ok(Some(owner))
321
329
  }
322
330
 
331
+ ///
323
332
  /// `apply_first_time_leader_binding`(`state.py:434`):命令 + workspace realpath 双门后,
324
333
  /// 写入 team_owner / leader_receiver(owner_epoch=0)。拒绝 dict 字节对齐 Python(含 `pane` +
325
334
  /// `repr()` 错误串)。`command_looks_usable`(`_leader_command_looks_usable`,step 11)经闭包注入。
@@ -411,6 +420,7 @@ pub fn apply_first_time_leader_binding(
411
420
  json!({ "ok": true, "pane": pane_info, "warning": null, "first_time": true })
412
421
  }
413
422
 
423
+ ///
414
424
  /// `leader_env_exports`(`state.py:469`):receiver + identity → 6 个 leader env 导出(插入序)。
415
425
  pub fn leader_env_exports(receiver: &Value, identity: &Value) -> Vec<(String, String)> {
416
426
  vec![
@@ -441,6 +451,7 @@ pub fn leader_env_exports(receiver: &Value, identity: &Value) -> Vec<(String, St
441
451
  ]
442
452
  }
443
453
 
454
+ ///
444
455
  /// `validate_leader_uuid_from_targets`(`state.py:480`):fake provider 直过;否则核对 targets
445
456
  /// 扫描成功 + receiver.pane_id 在 targets 中存在。
446
457
  pub fn validate_leader_uuid_from_targets(receiver: &Value, targets: &Value) -> Value {
@@ -1,3 +1,4 @@
1
+ //!
1
2
  //! Stage 0 of the identity-boundary unified plan (architect direction
2
3
  //! 2026-06-23): `SessionAttributionKey`.
3
4
  //!