kanibako-cli 1.6.0.dev36__tar.gz → 1.7.0rc0__tar.gz

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 (207) hide show
  1. {kanibako_cli-1.6.0.dev36/src/kanibako_cli.egg-info → kanibako_cli-1.7.0rc0}/PKG-INFO +1 -1
  2. {kanibako_cli-1.6.0.dev36 → kanibako_cli-1.7.0rc0}/README.md +31 -30
  3. {kanibako_cli-1.6.0.dev36 → kanibako_cli-1.7.0rc0}/pyproject.toml +1 -1
  4. kanibako_cli-1.7.0rc0/src/kanibako/__init__.py +25 -0
  5. {kanibako_cli-1.6.0.dev36 → kanibako_cli-1.7.0rc0}/src/kanibako/agent_config.py +14 -5
  6. {kanibako_cli-1.6.0.dev36 → kanibako_cli-1.7.0rc0}/src/kanibako/agent_defaults.py +1 -0
  7. kanibako_cli-1.7.0rc0/src/kanibako/agent_ref.py +168 -0
  8. {kanibako_cli-1.6.0.dev36 → kanibako_cli-1.7.0rc0}/src/kanibako/agent_representation.py +23 -9
  9. {kanibako_cli-1.6.0.dev36 → kanibako_cli-1.7.0rc0}/src/kanibako/channels.py +13 -13
  10. {kanibako_cli-1.6.0.dev36 → kanibako_cli-1.7.0rc0}/src/kanibako/cli.py +42 -31
  11. {kanibako_cli-1.6.0.dev36 → kanibako_cli-1.7.0rc0}/src/kanibako/commands/agent_cmd.py +197 -49
  12. {kanibako_cli-1.6.0.dev36 → kanibako_cli-1.7.0rc0}/src/kanibako/commands/box/__init__.py +7 -3
  13. {kanibako_cli-1.6.0.dev36 → kanibako_cli-1.7.0rc0}/src/kanibako/commands/box/_duplicate.py +5 -6
  14. {kanibako_cli-1.6.0.dev36 → kanibako_cli-1.7.0rc0}/src/kanibako/commands/box/_lifecycle.py +24 -24
  15. {kanibako_cli-1.6.0.dev36 → kanibako_cli-1.7.0rc0}/src/kanibako/commands/box/_parser.py +277 -72
  16. {kanibako_cli-1.6.0.dev36 → kanibako_cli-1.7.0rc0}/src/kanibako/commands/clean.py +9 -9
  17. {kanibako_cli-1.6.0.dev36 → kanibako_cli-1.7.0rc0}/src/kanibako/commands/diagnose.py +64 -2
  18. {kanibako_cli-1.6.0.dev36 → kanibako_cli-1.7.0rc0}/src/kanibako/commands/flags.py +4 -1
  19. {kanibako_cli-1.6.0.dev36 → kanibako_cli-1.7.0rc0}/src/kanibako/commands/install.py +11 -15
  20. {kanibako_cli-1.6.0.dev36 → kanibako_cli-1.7.0rc0}/src/kanibako/commands/setup_cmd.py +2 -2
  21. {kanibako_cli-1.6.0.dev36 → kanibako_cli-1.7.0rc0}/src/kanibako/commands/start.py +901 -178
  22. {kanibako_cli-1.6.0.dev36 → kanibako_cli-1.7.0rc0}/src/kanibako/commands/stop.py +30 -5
  23. {kanibako_cli-1.6.0.dev36 → kanibako_cli-1.7.0rc0}/src/kanibako/commands/system_cmd.py +112 -29
  24. {kanibako_cli-1.6.0.dev36 → kanibako_cli-1.7.0rc0}/src/kanibako/commands/workset_cmd.py +167 -135
  25. {kanibako_cli-1.6.0.dev36 → kanibako_cli-1.7.0rc0}/src/kanibako/config.py +157 -141
  26. {kanibako_cli-1.6.0.dev36 → kanibako_cli-1.7.0rc0}/src/kanibako/config_interface.py +502 -122
  27. {kanibako_cli-1.6.0.dev36 → kanibako_cli-1.7.0rc0}/src/kanibako/config_io.py +1 -1
  28. {kanibako_cli-1.6.0.dev36 → kanibako_cli-1.7.0rc0}/src/kanibako/core_defaults.py +31 -4
  29. {kanibako_cli-1.6.0.dev36 → kanibako_cli-1.7.0rc0}/src/kanibako/data/core-defaults.yaml +74 -2
  30. {kanibako_cli-1.6.0.dev36 → kanibako_cli-1.7.0rc0}/src/kanibako/helper_listener.py +13 -4
  31. {kanibako_cli-1.6.0.dev36 → kanibako_cli-1.7.0rc0}/src/kanibako/helpers.py +2 -2
  32. {kanibako_cli-1.6.0.dev36 → kanibako_cli-1.7.0rc0}/src/kanibako/import_reconcile.py +178 -38
  33. kanibako_cli-1.7.0rc0/src/kanibako/journal.py +168 -0
  34. {kanibako_cli-1.6.0.dev36 → kanibako_cli-1.7.0rc0}/src/kanibako/names.py +104 -36
  35. {kanibako_cli-1.6.0.dev36 → kanibako_cli-1.7.0rc0}/src/kanibako/paths.py +329 -177
  36. kanibako_cli-1.7.0rc0/src/kanibako/registry_store.py +194 -0
  37. {kanibako_cli-1.6.0.dev36 → kanibako_cli-1.7.0rc0}/src/kanibako/rig_registry.py +9 -7
  38. {kanibako_cli-1.6.0.dev36 → kanibako_cli-1.7.0rc0}/src/kanibako/settings_assemble.py +18 -20
  39. {kanibako_cli-1.6.0.dev36 → kanibako_cli-1.7.0rc0}/src/kanibako/settings_categories.py +19 -13
  40. {kanibako_cli-1.6.0.dev36 → kanibako_cli-1.7.0rc0}/src/kanibako/settings_configset.py +64 -81
  41. {kanibako_cli-1.6.0.dev36 → kanibako_cli-1.7.0rc0}/src/kanibako/settings_expand.py +124 -10
  42. kanibako_cli-1.7.0rc0/src/kanibako/settings_launch.py +1586 -0
  43. {kanibako_cli-1.6.0.dev36 → kanibako_cli-1.7.0rc0}/src/kanibako/settings_merge.py +28 -93
  44. {kanibako_cli-1.6.0.dev36 → kanibako_cli-1.7.0rc0}/src/kanibako/settings_resolve.py +20 -2
  45. {kanibako_cli-1.6.0.dev36 → kanibako_cli-1.7.0rc0}/src/kanibako/settings_views.py +99 -6
  46. {kanibako_cli-1.6.0.dev36 → kanibako_cli-1.7.0rc0}/src/kanibako/shells.py +6 -4
  47. {kanibako_cli-1.6.0.dev36 → kanibako_cli-1.7.0rc0}/src/kanibako/targets/__init__.py +4 -4
  48. {kanibako_cli-1.6.0.dev36 → kanibako_cli-1.7.0rc0}/src/kanibako/targets/base.py +4 -2
  49. kanibako_cli-1.7.0rc0/src/kanibako/targets/credsync.py +452 -0
  50. {kanibako_cli-1.6.0.dev36 → kanibako_cli-1.7.0rc0}/src/kanibako/templates.py +12 -12
  51. {kanibako_cli-1.6.0.dev36 → kanibako_cli-1.7.0rc0}/src/kanibako/workset.py +90 -82
  52. {kanibako_cli-1.6.0.dev36 → kanibako_cli-1.7.0rc0/src/kanibako_cli.egg-info}/PKG-INFO +1 -1
  53. {kanibako_cli-1.6.0.dev36 → kanibako_cli-1.7.0rc0}/src/kanibako_cli.egg-info/SOURCES.txt +6 -2
  54. {kanibako_cli-1.6.0.dev36 → kanibako_cli-1.7.0rc0}/tests/test_agent_config.py +41 -0
  55. kanibako_cli-1.7.0rc0/tests/test_agent_ref.py +211 -0
  56. {kanibako_cli-1.6.0.dev36 → kanibako_cli-1.7.0rc0}/tests/test_agent_representation.py +53 -0
  57. {kanibako_cli-1.6.0.dev36 → kanibako_cli-1.7.0rc0}/tests/test_agent_resolution.py +106 -14
  58. {kanibako_cli-1.6.0.dev36 → kanibako_cli-1.7.0rc0}/tests/test_channels.py +2 -2
  59. {kanibako_cli-1.6.0.dev36 → kanibako_cli-1.7.0rc0}/tests/test_cli.py +276 -50
  60. {kanibako_cli-1.6.0.dev36 → kanibako_cli-1.7.0rc0}/tests/test_cli_exitcodes.py +16 -8
  61. {kanibako_cli-1.6.0.dev36 → kanibako_cli-1.7.0rc0}/tests/test_comms.py +15 -17
  62. {kanibako_cli-1.6.0.dev36 → kanibako_cli-1.7.0rc0}/tests/test_config.py +295 -163
  63. {kanibako_cli-1.6.0.dev36 → kanibako_cli-1.7.0rc0}/tests/test_config_interface.py +537 -56
  64. kanibako_cli-1.7.0rc0/tests/test_create_recovery.py +506 -0
  65. {kanibako_cli-1.6.0.dev36 → kanibako_cli-1.7.0rc0}/tests/test_env_cmd.py +6 -6
  66. {kanibako_cli-1.6.0.dev36 → kanibako_cli-1.7.0rc0}/tests/test_fork.py +4 -3
  67. {kanibako_cli-1.6.0.dev36 → kanibako_cli-1.7.0rc0}/tests/test_helper_cmd.py +1 -1
  68. {kanibako_cli-1.6.0.dev36 → kanibako_cli-1.7.0rc0}/tests/test_helper_listener.py +13 -3
  69. {kanibako_cli-1.6.0.dev36 → kanibako_cli-1.7.0rc0}/tests/test_image_sharing.py +3 -3
  70. {kanibako_cli-1.6.0.dev36 → kanibako_cli-1.7.0rc0}/tests/test_import.py +36 -41
  71. kanibako_cli-1.7.0rc0/tests/test_import_recovery.py +646 -0
  72. {kanibako_cli-1.6.0.dev36 → kanibako_cli-1.7.0rc0}/tests/test_init_cmd.py +25 -52
  73. kanibako_cli-1.7.0rc0/tests/test_journal.py +147 -0
  74. {kanibako_cli-1.6.0.dev36 → kanibako_cli-1.7.0rc0}/tests/test_lifecycle_integration.py +3 -3
  75. {kanibako_cli-1.6.0.dev36 → kanibako_cli-1.7.0rc0}/tests/test_names.py +210 -128
  76. {kanibako_cli-1.6.0.dev36 → kanibako_cli-1.7.0rc0}/tests/test_paths.py +19 -58
  77. kanibako_cli-1.7.0rc0/tests/test_registry_store.py +268 -0
  78. {kanibako_cli-1.6.0.dev36 → kanibako_cli-1.7.0rc0}/tests/test_resolve_box_target.py +3 -3
  79. {kanibako_cli-1.6.0.dev36 → kanibako_cli-1.7.0rc0}/tests/test_resource_scoping.py +3 -3
  80. {kanibako_cli-1.6.0.dev36 → kanibako_cli-1.7.0rc0}/tests/test_rig_registry.py +1 -1
  81. {kanibako_cli-1.6.0.dev36 → kanibako_cli-1.7.0rc0}/tests/test_safety_invariants.py +2 -2
  82. {kanibako_cli-1.6.0.dev36 → kanibako_cli-1.7.0rc0}/tests/test_settings_assemble.py +25 -38
  83. {kanibako_cli-1.6.0.dev36 → kanibako_cli-1.7.0rc0}/tests/test_settings_categories.py +290 -29
  84. {kanibako_cli-1.6.0.dev36 → kanibako_cli-1.7.0rc0}/tests/test_settings_configset.py +185 -56
  85. {kanibako_cli-1.6.0.dev36 → kanibako_cli-1.7.0rc0}/tests/test_settings_expand.py +167 -0
  86. kanibako_cli-1.7.0rc0/tests/test_settings_launch.py +1144 -0
  87. {kanibako_cli-1.6.0.dev36 → kanibako_cli-1.7.0rc0}/tests/test_settings_launch_equivalence.py +68 -10
  88. {kanibako_cli-1.6.0.dev36 → kanibako_cli-1.7.0rc0}/tests/test_settings_merge.py +31 -102
  89. {kanibako_cli-1.6.0.dev36 → kanibako_cli-1.7.0rc0}/tests/test_settings_views.py +1 -4
  90. {kanibako_cli-1.6.0.dev36 → kanibako_cli-1.7.0rc0}/tests/test_shells.py +7 -1
  91. {kanibako_cli-1.6.0.dev36 → kanibako_cli-1.7.0rc0}/tests/test_standalone_paths.py +2 -2
  92. {kanibako_cli-1.6.0.dev36 → kanibako_cli-1.7.0rc0}/tests/test_system_paths.py +247 -120
  93. {kanibako_cli-1.6.0.dev36 → kanibako_cli-1.7.0rc0}/tests/test_workset.py +49 -101
  94. {kanibako_cli-1.6.0.dev36 → kanibako_cli-1.7.0rc0}/tests/test_workset_paths.py +0 -79
  95. kanibako_cli-1.6.0.dev36/src/kanibako/__init__.py +0 -11
  96. kanibako_cli-1.6.0.dev36/src/kanibako/box_seed.py +0 -84
  97. kanibako_cli-1.6.0.dev36/src/kanibako/registry_store.py +0 -285
  98. kanibako_cli-1.6.0.dev36/src/kanibako/settings_launch.py +0 -734
  99. kanibako_cli-1.6.0.dev36/src/kanibako/targets/credsync.py +0 -208
  100. kanibako_cli-1.6.0.dev36/tests/test_box_seed.py +0 -137
  101. kanibako_cli-1.6.0.dev36/tests/test_registry_store.py +0 -238
  102. kanibako_cli-1.6.0.dev36/tests/test_settings_launch.py +0 -354
  103. {kanibako_cli-1.6.0.dev36 → kanibako_cli-1.7.0rc0}/LICENSE.md +0 -0
  104. {kanibako_cli-1.6.0.dev36 → kanibako_cli-1.7.0rc0}/setup.cfg +0 -0
  105. {kanibako_cli-1.6.0.dev36 → kanibako_cli-1.7.0rc0}/src/kanibako/__main__.py +0 -0
  106. {kanibako_cli-1.6.0.dev36 → kanibako_cli-1.7.0rc0}/src/kanibako/_atomic.py +0 -0
  107. {kanibako_cli-1.6.0.dev36 → kanibako_cli-1.7.0rc0}/src/kanibako/auth_browser.py +0 -0
  108. {kanibako_cli-1.6.0.dev36 → kanibako_cli-1.7.0rc0}/src/kanibako/auth_parser.py +0 -0
  109. {kanibako_cli-1.6.0.dev36 → kanibako_cli-1.7.0rc0}/src/kanibako/baseline.py +0 -0
  110. {kanibako_cli-1.6.0.dev36 → kanibako_cli-1.7.0rc0}/src/kanibako/box_identity.py +0 -0
  111. {kanibako_cli-1.6.0.dev36 → kanibako_cli-1.7.0rc0}/src/kanibako/browser_sidecar.py +0 -0
  112. {kanibako_cli-1.6.0.dev36 → kanibako_cli-1.7.0rc0}/src/kanibako/browser_state.py +0 -0
  113. {kanibako_cli-1.6.0.dev36 → kanibako_cli-1.7.0rc0}/src/kanibako/bun_sea.py +0 -0
  114. {kanibako_cli-1.6.0.dev36 → kanibako_cli-1.7.0rc0}/src/kanibako/commands/__init__.py +0 -0
  115. {kanibako_cli-1.6.0.dev36 → kanibako_cli-1.7.0rc0}/src/kanibako/commands/archive.py +0 -0
  116. {kanibako_cli-1.6.0.dev36 → kanibako_cli-1.7.0rc0}/src/kanibako/commands/baseline_cmd.py +0 -0
  117. {kanibako_cli-1.6.0.dev36 → kanibako_cli-1.7.0rc0}/src/kanibako/commands/fork_cmd.py +0 -0
  118. {kanibako_cli-1.6.0.dev36 → kanibako_cli-1.7.0rc0}/src/kanibako/commands/helper_cmd.py +0 -0
  119. {kanibako_cli-1.6.0.dev36 → kanibako_cli-1.7.0rc0}/src/kanibako/commands/image.py +0 -0
  120. {kanibako_cli-1.6.0.dev36 → kanibako_cli-1.7.0rc0}/src/kanibako/commands/restore.py +0 -0
  121. {kanibako_cli-1.6.0.dev36 → kanibako_cli-1.7.0rc0}/src/kanibako/commands/upgrade.py +0 -0
  122. {kanibako_cli-1.6.0.dev36 → kanibako_cli-1.7.0rc0}/src/kanibako/commands/vault_cmd.py +0 -0
  123. {kanibako_cli-1.6.0.dev36 → kanibako_cli-1.7.0rc0}/src/kanibako/container.py +0 -0
  124. {kanibako_cli-1.6.0.dev36 → kanibako_cli-1.7.0rc0}/src/kanibako/containerfiles.py +0 -0
  125. {kanibako_cli-1.6.0.dev36 → kanibako_cli-1.7.0rc0}/src/kanibako/containers/Containerfile.template-android +0 -0
  126. {kanibako_cli-1.6.0.dev36 → kanibako_cli-1.7.0rc0}/src/kanibako/containers/Containerfile.template-dotnet +0 -0
  127. {kanibako_cli-1.6.0.dev36 → kanibako_cli-1.7.0rc0}/src/kanibako/containers/Containerfile.template-js +0 -0
  128. {kanibako_cli-1.6.0.dev36 → kanibako_cli-1.7.0rc0}/src/kanibako/containers/Containerfile.template-jvm +0 -0
  129. {kanibako_cli-1.6.0.dev36 → kanibako_cli-1.7.0rc0}/src/kanibako/containers/Containerfile.template-systems +0 -0
  130. {kanibako_cli-1.6.0.dev36 → kanibako_cli-1.7.0rc0}/src/kanibako/containers/__init__.py +0 -0
  131. {kanibako_cli-1.6.0.dev36 → kanibako_cli-1.7.0rc0}/src/kanibako/containers/tmux.conf +0 -0
  132. {kanibako_cli-1.6.0.dev36 → kanibako_cli-1.7.0rc0}/src/kanibako/data/__init__.py +0 -0
  133. {kanibako_cli-1.6.0.dev36 → kanibako_cli-1.7.0rc0}/src/kanibako/data/image-baseline.yaml +0 -0
  134. {kanibako_cli-1.6.0.dev36 → kanibako_cli-1.7.0rc0}/src/kanibako/data/templates/base/INSTRUCTIONS.md +0 -0
  135. {kanibako_cli-1.6.0.dev36 → kanibako_cli-1.7.0rc0}/src/kanibako/deprecation.py +0 -0
  136. {kanibako_cli-1.6.0.dev36 → kanibako_cli-1.7.0rc0}/src/kanibako/errors.py +0 -0
  137. {kanibako_cli-1.6.0.dev36 → kanibako_cli-1.7.0rc0}/src/kanibako/freshness.py +0 -0
  138. {kanibako_cli-1.6.0.dev36 → kanibako_cli-1.7.0rc0}/src/kanibako/git.py +0 -0
  139. {kanibako_cli-1.6.0.dev36 → kanibako_cli-1.7.0rc0}/src/kanibako/helper_client.py +0 -0
  140. {kanibako_cli-1.6.0.dev36 → kanibako_cli-1.7.0rc0}/src/kanibako/hygiene.py +0 -0
  141. {kanibako_cli-1.6.0.dev36 → kanibako_cli-1.7.0rc0}/src/kanibako/image_sharing.py +0 -0
  142. {kanibako_cli-1.6.0.dev36 → kanibako_cli-1.7.0rc0}/src/kanibako/install_method.py +0 -0
  143. {kanibako_cli-1.6.0.dev36 → kanibako_cli-1.7.0rc0}/src/kanibako/log.py +0 -0
  144. {kanibako_cli-1.6.0.dev36 → kanibako_cli-1.7.0rc0}/src/kanibako/plugins/__init__.py +0 -0
  145. {kanibako_cli-1.6.0.dev36 → kanibako_cli-1.7.0rc0}/src/kanibako/py.typed +0 -0
  146. {kanibako_cli-1.6.0.dev36 → kanibako_cli-1.7.0rc0}/src/kanibako/registry.py +0 -0
  147. {kanibako_cli-1.6.0.dev36 → kanibako_cli-1.7.0rc0}/src/kanibako/rig_bundle.py +0 -0
  148. {kanibako_cli-1.6.0.dev36 → kanibako_cli-1.7.0rc0}/src/kanibako/rig_meta.py +0 -0
  149. {kanibako_cli-1.6.0.dev36 → kanibako_cli-1.7.0rc0}/src/kanibako/rig_resolve.py +0 -0
  150. {kanibako_cli-1.6.0.dev36 → kanibako_cli-1.7.0rc0}/src/kanibako/rig_source.py +0 -0
  151. {kanibako_cli-1.6.0.dev36 → kanibako_cli-1.7.0rc0}/src/kanibako/scripts/__init__.py +0 -0
  152. {kanibako_cli-1.6.0.dev36 → kanibako_cli-1.7.0rc0}/src/kanibako/scripts/helper-init.sh +0 -0
  153. {kanibako_cli-1.6.0.dev36 → kanibako_cli-1.7.0rc0}/src/kanibako/scripts/kanibako-entry +0 -0
  154. {kanibako_cli-1.6.0.dev36 → kanibako_cli-1.7.0rc0}/src/kanibako/settings_store.py +0 -0
  155. {kanibako_cli-1.6.0.dev36 → kanibako_cli-1.7.0rc0}/src/kanibako/shellenv.py +0 -0
  156. {kanibako_cli-1.6.0.dev36 → kanibako_cli-1.7.0rc0}/src/kanibako/snapshots.py +0 -0
  157. {kanibako_cli-1.6.0.dev36 → kanibako_cli-1.7.0rc0}/src/kanibako/targets/assembly.py +0 -0
  158. {kanibako_cli-1.6.0.dev36 → kanibako_cli-1.7.0rc0}/src/kanibako/targets/no_agent.py +0 -0
  159. {kanibako_cli-1.6.0.dev36 → kanibako_cli-1.7.0rc0}/src/kanibako/templates_image.py +0 -0
  160. {kanibako_cli-1.6.0.dev36 → kanibako_cli-1.7.0rc0}/src/kanibako/tweakcc.py +0 -0
  161. {kanibako_cli-1.6.0.dev36 → kanibako_cli-1.7.0rc0}/src/kanibako/tweakcc_cache.py +0 -0
  162. {kanibako_cli-1.6.0.dev36 → kanibako_cli-1.7.0rc0}/src/kanibako/utils.py +0 -0
  163. {kanibako_cli-1.6.0.dev36 → kanibako_cli-1.7.0rc0}/src/kanibako_cli.egg-info/dependency_links.txt +0 -0
  164. {kanibako_cli-1.6.0.dev36 → kanibako_cli-1.7.0rc0}/src/kanibako_cli.egg-info/entry_points.txt +0 -0
  165. {kanibako_cli-1.6.0.dev36 → kanibako_cli-1.7.0rc0}/src/kanibako_cli.egg-info/requires.txt +0 -0
  166. {kanibako_cli-1.6.0.dev36 → kanibako_cli-1.7.0rc0}/src/kanibako_cli.egg-info/top_level.txt +0 -0
  167. {kanibako_cli-1.6.0.dev36 → kanibako_cli-1.7.0rc0}/tests/test_archive_restore_integration.py +0 -0
  168. {kanibako_cli-1.6.0.dev36 → kanibako_cli-1.7.0rc0}/tests/test_atomic.py +0 -0
  169. {kanibako_cli-1.6.0.dev36 → kanibako_cli-1.7.0rc0}/tests/test_auth_browser.py +0 -0
  170. {kanibako_cli-1.6.0.dev36 → kanibako_cli-1.7.0rc0}/tests/test_auth_parser.py +0 -0
  171. {kanibako_cli-1.6.0.dev36 → kanibako_cli-1.7.0rc0}/tests/test_baseline.py +0 -0
  172. {kanibako_cli-1.6.0.dev36 → kanibako_cli-1.7.0rc0}/tests/test_box_identity.py +0 -0
  173. {kanibako_cli-1.6.0.dev36 → kanibako_cli-1.7.0rc0}/tests/test_browser_sidecar.py +0 -0
  174. {kanibako_cli-1.6.0.dev36 → kanibako_cli-1.7.0rc0}/tests/test_browser_state.py +0 -0
  175. {kanibako_cli-1.6.0.dev36 → kanibako_cli-1.7.0rc0}/tests/test_bun_sea.py +0 -0
  176. {kanibako_cli-1.6.0.dev36 → kanibako_cli-1.7.0rc0}/tests/test_container.py +0 -0
  177. {kanibako_cli-1.6.0.dev36 → kanibako_cli-1.7.0rc0}/tests/test_container_extended.py +0 -0
  178. {kanibako_cli-1.6.0.dev36 → kanibako_cli-1.7.0rc0}/tests/test_container_integration.py +0 -0
  179. {kanibako_cli-1.6.0.dev36 → kanibako_cli-1.7.0rc0}/tests/test_containerfiles.py +0 -0
  180. {kanibako_cli-1.6.0.dev36 → kanibako_cli-1.7.0rc0}/tests/test_credentials.py +0 -0
  181. {kanibako_cli-1.6.0.dev36 → kanibako_cli-1.7.0rc0}/tests/test_defaults_golden.py +0 -0
  182. {kanibako_cli-1.6.0.dev36 → kanibako_cli-1.7.0rc0}/tests/test_deprecations.py +0 -0
  183. {kanibako_cli-1.6.0.dev36 → kanibako_cli-1.7.0rc0}/tests/test_error_messages.py +0 -0
  184. {kanibako_cli-1.6.0.dev36 → kanibako_cli-1.7.0rc0}/tests/test_freshness.py +0 -0
  185. {kanibako_cli-1.6.0.dev36 → kanibako_cli-1.7.0rc0}/tests/test_git.py +0 -0
  186. {kanibako_cli-1.6.0.dev36 → kanibako_cli-1.7.0rc0}/tests/test_helper_client.py +0 -0
  187. {kanibako_cli-1.6.0.dev36 → kanibako_cli-1.7.0rc0}/tests/test_helpers.py +0 -0
  188. {kanibako_cli-1.6.0.dev36 → kanibako_cli-1.7.0rc0}/tests/test_hygiene.py +0 -0
  189. {kanibako_cli-1.6.0.dev36 → kanibako_cli-1.7.0rc0}/tests/test_install_integration.py +0 -0
  190. {kanibako_cli-1.6.0.dev36 → kanibako_cli-1.7.0rc0}/tests/test_log.py +0 -0
  191. {kanibako_cli-1.6.0.dev36 → kanibako_cli-1.7.0rc0}/tests/test_paths_extended.py +0 -0
  192. {kanibako_cli-1.6.0.dev36 → kanibako_cli-1.7.0rc0}/tests/test_registry.py +0 -0
  193. {kanibako_cli-1.6.0.dev36 → kanibako_cli-1.7.0rc0}/tests/test_rig_bundle.py +0 -0
  194. {kanibako_cli-1.6.0.dev36 → kanibako_cli-1.7.0rc0}/tests/test_rig_meta.py +0 -0
  195. {kanibako_cli-1.6.0.dev36 → kanibako_cli-1.7.0rc0}/tests/test_rig_resolve.py +0 -0
  196. {kanibako_cli-1.6.0.dev36 → kanibako_cli-1.7.0rc0}/tests/test_rig_source.py +0 -0
  197. {kanibako_cli-1.6.0.dev36 → kanibako_cli-1.7.0rc0}/tests/test_settings_resolve.py +0 -0
  198. {kanibako_cli-1.6.0.dev36 → kanibako_cli-1.7.0rc0}/tests/test_settings_store.py +0 -0
  199. {kanibako_cli-1.6.0.dev36 → kanibako_cli-1.7.0rc0}/tests/test_setup_cmd.py +0 -0
  200. {kanibako_cli-1.6.0.dev36 → kanibako_cli-1.7.0rc0}/tests/test_shellenv.py +0 -0
  201. {kanibako_cli-1.6.0.dev36 → kanibako_cli-1.7.0rc0}/tests/test_snapshots.py +0 -0
  202. {kanibako_cli-1.6.0.dev36 → kanibako_cli-1.7.0rc0}/tests/test_start_integration.py +0 -0
  203. {kanibako_cli-1.6.0.dev36 → kanibako_cli-1.7.0rc0}/tests/test_status.py +0 -0
  204. {kanibako_cli-1.6.0.dev36 → kanibako_cli-1.7.0rc0}/tests/test_templates.py +0 -0
  205. {kanibako_cli-1.6.0.dev36 → kanibako_cli-1.7.0rc0}/tests/test_templates_image.py +0 -0
  206. {kanibako_cli-1.6.0.dev36 → kanibako_cli-1.7.0rc0}/tests/test_tweakcc.py +0 -0
  207. {kanibako_cli-1.6.0.dev36 → kanibako_cli-1.7.0rc0}/tests/test_utils.py +0 -0
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: kanibako-cli
3
- Version: 1.6.0.dev36
3
+ Version: 1.7.0rc0
4
4
  Summary: Run AI coding agents in rootless containers with per-project isolation
5
5
  Requires-Python: >=3.11
6
6
  License-File: LICENSE.md
@@ -39,12 +39,12 @@ by the `kanibako` meta-package); other agents can be added as plugins.
39
39
  - **Vault snapshots** -- per-box read-only and read-write shared
40
40
  directories with smart snapshot strategy detection (reflink, hardlink,
41
41
  or tar.xz depending on filesystem)
42
- - **Shell customization** -- per-box environment variables (`box config
42
+ - **Shell customization** -- per-box environment variables (`box set
43
43
  env.*`), drop-in init scripts (`shell.d/`), and layered seed-once home
44
44
  templates
45
45
  - **Agent configuration** -- per-agent YAML config with default args, state
46
46
  knobs, env vars, and shared caches; per-box setting overrides via
47
- `box config`
47
+ `box set`
48
48
  - **Shared caches** -- global download caches (pip, cargo, npm, etc.)
49
49
  shared across boxes; agent-level caches via the agent config
50
50
  - **Plugin system** -- agent-agnostic core (`kanibako-cli`); Claude/Codex/Goose
@@ -220,7 +220,7 @@ shortcuts for common operations:
220
220
  | `box list` / `box ls` | List projects (`--all`, `--orphan`, `-q`) |
221
221
  | `box info` / `box inspect` | Project details (mode, paths, lock, rig) |
222
222
  | `box rm` / `box delete` | Remove project (`--purge` deletes metadata, `--force` skips confirm) |
223
- | `box config` | View or modify project configuration |
223
+ | `box set` / `box get` / `box show` / `box reset` | View or modify project configuration |
224
224
  | `box diagnose [project]` | Check project box health |
225
225
 
226
226
  **Relocation & conversion:**
@@ -265,7 +265,7 @@ shortcuts for common operations:
265
265
  | `workset list` / `workset ls` | List working sets (`-q`) |
266
266
  | `workset info` / `workset inspect` | Working set details |
267
267
  | `workset rm` / `workset delete` | Remove working set (`--purge`, `--force`) |
268
- | `workset config` | View or modify workset configuration (use `workset config default <key>=<value>` for default-workset defaults) |
268
+ | `workset set` / `workset get` / `workset show` / `workset reset` | View or modify workset configuration (use `workset set default <key>=<value>` for default-workset defaults) |
269
269
  | `workset connect <workset> [source]` | Add project to working set (`--name`) |
270
270
  | `workset disconnect <workset> <project>` | Remove project from working set (`--force`) |
271
271
 
@@ -275,7 +275,7 @@ shortcuts for common operations:
275
275
  |------------|-------------|
276
276
  | `agent list` / `agent ls` | List configured agents (`-q`) |
277
277
  | `agent info` / `agent inspect` | Agent configuration details |
278
- | `agent config` | View or modify agent configuration |
278
+ | `agent set` / `agent get` / `agent show` / `agent reset` | View or modify agent configuration |
279
279
  | `agent reauth [project]` | Refresh credentials |
280
280
 
281
281
  ### `box helper` / `box fork` Subcommands
@@ -300,7 +300,7 @@ The runtime helper and fork verbs (formerly under `crab`) now live under `box`:
300
300
  | Subcommand | Description |
301
301
  |------------|-------------|
302
302
  | `system info` / `system inspect` | System details (version, runtime, paths) |
303
- | `system config` | View or modify global configuration |
303
+ | `system set` / `system get` / `system show` / `system reset` | View or modify global configuration |
304
304
  | `system upgrade` | Self-update (`--check` for dry run) |
305
305
  | `system diagnose` | Check system health (runtime, images, agents, storage) |
306
306
 
@@ -349,7 +349,7 @@ error, not a silent no-op).
349
349
  #### `--box`: operate on any box
350
350
 
351
351
  `--box` substitutes for being in the box's directory: `kanibako stop --box myproj`,
352
- `kanibako box config --box myproj model=opus`. The value is a **box name (resolved
352
+ `kanibako box set --box myproj model=opus`. The value is a **box name (resolved
353
353
  first) or a path**. It is the *subject* the command acts on, and stays orthogonal to
354
354
  the `move`/`convert` *destination* group, so they coexist:
355
355
 
@@ -408,7 +408,7 @@ confirm. Non-TTY runs (CI / headless) skip the prompt gracefully.
408
408
  nudge to run `setup` if it has never been run, then proceed.
409
409
 
410
410
  Because `system.*` keys are **file-only** (see [Configuration](#configuration)),
411
- the default agent is *not* settable via `kanibako system config` -- use `setup` or
411
+ the default agent is *not* settable via `kanibako system set` -- use `setup` or
412
412
  edit `global/settings.yaml` directly.
413
413
 
414
414
  ## Project Modes
@@ -431,11 +431,11 @@ and run `kanibako`, and the box joins the primary workset.
431
431
 
432
432
  The primary workset is addressable through the same `workset` commands as named
433
433
  worksets (workset name token `__PRIMARY__`), so primary-workset settings use the
434
- ordinary `workset config default` mechanism (see [Configuration](#configuration)):
434
+ ordinary `workset set default` mechanism (see [Configuration](#configuration)):
435
435
 
436
436
  ```bash
437
- kanibako workset config default model=opus # default for ALL primary-mode boxes
438
- kanibako workset config default group_auth=false # distinct credentials by default
437
+ kanibako workset set default model=opus # default for ALL primary-mode boxes
438
+ kanibako workset set default group_auth=false # distinct credentials by default
439
439
  ```
440
440
 
441
441
  The names `__PRIMARY__` / `__STANDALONE__` (and legacy `default`) are reserved
@@ -629,9 +629,9 @@ container:
629
629
 
630
630
  ```bash
631
631
  # Persistent (stored in project config)
632
- kanibako box config env.EDITOR=vim # project-level
633
- kanibako system config env.EDITOR=nano # global (all projects)
634
- kanibako box config env.EDITOR # show one value
632
+ kanibako box set env.EDITOR=vim # project-level
633
+ kanibako system set env.EDITOR=nano # global (all projects)
634
+ kanibako box get env.EDITOR # show one value
635
635
 
636
636
  # Per-run (not persisted)
637
637
  kanibako start -e EDITOR=vim -e DEBUG=1
@@ -644,7 +644,7 @@ Project env vars override global ones.
644
644
  The shell prompt is controlled by the `KANIBAKO_PS1` environment variable:
645
645
 
646
646
  ```bash
647
- kanibako box config env.KANIBAKO_PS1="(myproject) \u:\w\$ "
647
+ kanibako box set env.KANIBAKO_PS1="(myproject) \u:\w\$ "
648
648
  ```
649
649
 
650
650
  ### Init scripts
@@ -702,8 +702,8 @@ Manage agent settings via the CLI:
702
702
 
703
703
  ```bash
704
704
  kanibako agent list # list configured agents
705
- kanibako agent config model # show effective model
706
- kanibako agent config model=sonnet # set agent-level default
705
+ kanibako agent get claude model # show the agent's model
706
+ kanibako agent set claude model=sonnet # set agent-level default
707
707
  ```
708
708
 
709
709
  ## Home Templates
@@ -822,7 +822,7 @@ guide.
822
822
  pip install kanibako-target-myagent
823
823
 
824
824
  # Use a specific target
825
- kanibako box config box.agent=myagent
825
+ kanibako box set box.agent=myagent
826
826
  kanibako start
827
827
 
828
828
  # (`crab_name` is gone -- select the agent via box.agent)
@@ -846,29 +846,30 @@ CLI flag > settings_required > box > workset > agent.<agent> > system > settings
846
846
  **file-only** -- the CLI reads and shows them but refuses to set them (edit the
847
847
  file directly); `setup` and programmatic writers still write them.
848
848
 
849
- All settings levels share a unified interface:
849
+ All settings levels share the same four verbs -- `set` / `get` / `show` /
850
+ `reset`:
850
851
 
851
852
  ```bash
852
853
  # Box level
853
- kanibako box config # show box overrides
854
- kanibako box config --effective # show resolved values (inherited + overrides)
855
- kanibako box config model # get one key
856
- kanibako box config model=sonnet # set one key
857
- kanibako box config --reset model # remove override, back to default
854
+ kanibako box show # show box overrides
855
+ kanibako box show --effective # show resolved values (inherited + overrides)
856
+ kanibako box get model # get one key
857
+ kanibako box set model=sonnet # set one key
858
+ kanibako box reset model # remove override, back to default
858
859
 
859
860
  # Workset level (group defaults inherited by member boxes)
860
- kanibako workset config <workset> model=opus
861
- kanibako workset config default model=opus # primary-workset default
861
+ kanibako workset set <workset> model=opus
862
+ kanibako workset set default model=opus # primary-workset default
862
863
 
863
864
  # Agent level (defaults for all boxes using this agent)
864
- kanibako agent config model=opus
865
+ kanibako agent set claude model=opus
865
866
 
866
867
  # System level (global settings defaults)
867
- kanibako system config model=opus
868
- kanibako system config --reset --all # reset all global settings
868
+ kanibako system set model=opus
869
+ kanibako system reset --all # reset all global settings
869
870
  ```
870
871
 
871
- `kanibako system config` sets **non-`system.`** settings only. `system.*`-prefixed
872
+ `kanibako system set` sets **non-`system.`** settings only. `system.*`-prefixed
872
873
  keys (layout paths AND `system.default_agent`) are **file-only**: the CLI shows them
873
874
  but refuses to set/reset them, pointing you at the config file. Set the default agent
874
875
  with `kanibako setup`; edit structural paths in `~/.config/kanibako.yaml` directly.
@@ -4,7 +4,7 @@ build-backend = "setuptools.build_meta"
4
4
 
5
5
  [project]
6
6
  name = "kanibako-cli"
7
- version = "1.6.0.dev36"
7
+ version = "1.7.0rc0"
8
8
  description = "Run AI coding agents in rootless containers with per-project isolation"
9
9
  requires-python = ">=3.11"
10
10
  dependencies = ["argcomplete>=3.0", "PyYAML>=6.0", "packaging>=20.0"]
@@ -0,0 +1,25 @@
1
+ """kanibako: Run AI coding agents in rootless containers with per-project isolation."""
2
+
3
+ __version__ = "1.7.0rc0"
4
+
5
+ # Two-tier setup/config compatibility constants for the 5-band setup-completion
6
+ # gate (design: ``plans/2026-06-23-setup-version-tiers-NEXT.md``). Both are
7
+ # compared against the recorded ``system.setup_completed`` marker by BASE version
8
+ # (PEP 440 via ``packaging.version``), so a dev/rc build of the same base reads
9
+ # as the released base, not "from the future".
10
+ #
11
+ # * ``SETUP_BCV`` — backward-compatible version: the OLDEST setup/config version
12
+ # this build can function with AT ALL. Below it, the build can no longer
13
+ # auto-fill the gaps → the gate ERRORS (rc1) and the user must re-run setup.
14
+ # Bump only when a setup change can no longer be auto-filled.
15
+ # * ``SETUP_FCV`` — forward-compatible version: the oldest version whose setup is
16
+ # COMPLETELY compatible (nothing new since). At or above it (but below the
17
+ # running build) the gate SILENTLY bumps the marker forward. Between BCV and
18
+ # FCV the gate NUDGES (non-blocking) to re-run setup. Bump whenever setup adds
19
+ # a new feature/case.
20
+ #
21
+ # Invariant: ``SETUP_BCV <= SETUP_FCV <= __version__`` (base versions). Most
22
+ # releases change setup in no way → bump NEITHER → existing configs land
23
+ # ``>= FCV`` → silent.
24
+ SETUP_BCV = "1.6.0"
25
+ SETUP_FCV = "1.7.0"
@@ -16,15 +16,19 @@ class AgentConfig:
16
16
  """Per-agent configuration loaded from an agent YAML file.
17
17
 
18
18
  Sections:
19
- agent — identity (name, run_args) plus agent-state knobs
20
- (model, access, start_mode, autonomous, …)
21
- env — raw env vars injected into container
19
+ agent — identity (name, run_args) plus agent-state knobs
20
+ (model, access, start_mode, autonomous, …)
21
+ env — raw env vars injected into container (VAR -> value)
22
+ env_file — env-from-file pointers (VAR -> host PATH): at launch the
23
+ file's contents become the env var's VALUE (secret stays in
24
+ the host file, only the path is stored — spec §2d).
22
25
  """
23
26
 
24
27
  name: str = ""
25
28
  run_args: list[str] = field(default_factory=list)
26
29
  state: dict[str, str] = field(default_factory=dict)
27
30
  env: dict[str, str] = field(default_factory=dict)
31
+ env_file: dict[str, str] = field(default_factory=dict)
28
32
  tweakcc: dict = field(default_factory=dict)
29
33
 
30
34
 
@@ -34,10 +38,10 @@ def agents_dir(data_path: Path, paths_agents: str = "agents") -> Path:
34
38
 
35
39
 
36
40
  def agent_settings_path(agents_root: Path, agent_id: str) -> Path:
37
- """Return ``@agent.<agent>.meta.settings`` for *agent_id*.
41
+ """Return ``@meta.agent.<agent>.settings`` for *agent_id*.
38
42
 
39
43
  The per-agent SETTINGS cascade file lives INSIDE the per-agent store dir
40
- (``@agent.<agent>.meta.path`` = ``agents/<agent>/``) as ``settings.yaml``
44
+ (``@meta.agent.<agent>.path`` = ``agents/<agent>/``) as ``settings.yaml``
41
45
  — NOT the old sibling ``agents/<agent>.yaml`` file (D-2026-06-22). This
42
46
  parallels the per-agent template dir ``agents/<agent>/template`` and the
43
47
  Part-3 ``agents/<agent>/{plugins,cache}`` stores.
@@ -76,6 +80,10 @@ def load_agent_config(path: Path) -> AgentConfig:
76
80
  k: str(v) for k, v in agent_sec.items() if k not in IDENTITY_KEYS
77
81
  }
78
82
  cfg.env = {k: str(v) for k, v in data.get("env", {}).items()}
83
+ # env_file: VAR -> host PATH pointer (the token file). Stored as a plain
84
+ # string path; the file's CONTENTS (the secret) are never persisted here —
85
+ # they are read into the container env only at launch (spec §2d).
86
+ cfg.env_file = {k: str(v) for k, v in data.get("env_file", {}).items()}
79
87
  cfg.tweakcc = dict(data.get("tweakcc", {}))
80
88
 
81
89
  return cfg
@@ -93,6 +101,7 @@ def write_agent_config(path: Path, cfg: AgentConfig) -> None:
93
101
  data: dict = {
94
102
  "agent": agent_sec,
95
103
  "env": dict(cfg.env),
104
+ "env_file": dict(cfg.env_file),
96
105
  "tweakcc": dict(cfg.tweakcc),
97
106
  }
98
107
  # The settings file lives inside the per-agent store dir
@@ -159,6 +159,7 @@ def load_descriptor(package: str, filename: str) -> PluginDescriptor:
159
159
  cred_files=tuple(_build_cred_file(c) for c in desc.get("cred_files", [])),
160
160
  host_prep=bool(desc.get("host_prep", False)),
161
161
  init_dirs=tuple(desc.get("init_dirs", ())),
162
+ auth_share_support=bool(desc.get("auth_share_support", False)),
162
163
  )
163
164
 
164
165
 
@@ -0,0 +1,168 @@
1
+ """Agent-ref parsing: the ``persona+harness`` selection grammar (rider MVP).
2
+
3
+ An ``--agent`` value is a *ref* that names an agent as ``persona+harness`` —
4
+ for example ``navigator+claude``. This module is the single, dependency-light,
5
+ pure parser for that grammar; every ref source (explicit ``--agent``, box /
6
+ workset / system settings) is normalised through here on the way into the
7
+ resolver so the rest of the code deals only in *node-names*.
8
+
9
+ Terminology (design SOT ``plans/2026-06-24-agent-variant-DESIGN.md``):
10
+
11
+ * **harness** — the agent runtime/plugin, right of the separator (``claude``).
12
+ Resolves the target/plugin (``resolve_target`` / ``discover_targets`` key).
13
+ * **persona** — the identity, left of the separator (``navigator``).
14
+ * **node-name** — the canonical fused form ``persona℘harness`` (``navigator℘claude``).
15
+ This is the on-disk ``agents/<node>/`` dir and the keyspace ``agent.<node>.*``
16
+ slot. It is what ``resolve_agent`` returns and what ``KANIBAKO_AGENT`` stamps.
17
+
18
+ Separators: the human-typable ``+`` (U+002B) and the canonical ``℘`` (U+2118,
19
+ SCRIPT CAPITAL P — "persona"). Both are accepted on input; the node-name always
20
+ canonicalises the separator to ``℘``. Only the FIRST separator splits (a persona
21
+ segment may not itself contain a separator; the harness segment is whatever
22
+ follows, verbatim, and is likewise separator-checked).
23
+
24
+ Backward compatibility (LOAD-BEARING): a bare ref with no separator (``claude``)
25
+ parses to ``(raw, raw)`` — node == harness == the bare name — so every bare path
26
+ is byte-for-byte identical to pre-rider behaviour.
27
+ """
28
+
29
+ from __future__ import annotations
30
+
31
+ from kanibako.errors import ConfigError
32
+
33
+ #: The canonical persona/harness separator (U+2118 SCRIPT CAPITAL P).
34
+ CANONICAL_SEP = "℘" # ℘
35
+ #: The human-typable separator (U+002B PLUS SIGN).
36
+ PLUS_SEP = "+"
37
+ #: Both accepted separators.
38
+ SEPARATORS = (PLUS_SEP, CANONICAL_SEP)
39
+
40
+ #: Characters permitted in a persona OR harness segment (fs- and key-safe).
41
+ #: Alphanumeric plus ``-``, ``.``, ``_`` — NO separators, NO path/whitespace.
42
+ _SAFE_EXTRA = frozenset("-._")
43
+
44
+
45
+ def _is_segment_safe(segment: str) -> bool:
46
+ """A non-empty segment of only ``[A-Za-z0-9]`` plus ``-`` / ``.`` / ``_``."""
47
+ if not segment:
48
+ return False
49
+ return all(ch.isalnum() or ch in _SAFE_EXTRA for ch in segment)
50
+
51
+
52
+ def _first_sep_index(raw: str) -> int:
53
+ """Index of the FIRST separator in *raw*, or ``-1`` if none."""
54
+ indices = [raw.find(sep) for sep in SEPARATORS]
55
+ present = [i for i in indices if i != -1]
56
+ return min(present) if present else -1
57
+
58
+
59
+ def parse_agent_ref(raw: str) -> tuple[str, str]:
60
+ """Parse an agent ref into ``(node, harness)``.
61
+
62
+ * Bare (no separator): ``"claude"`` -> ``("claude", "claude")`` — node and
63
+ harness are the same, preserving backward compatibility.
64
+ * Composite: ``"navigator+claude"`` -> ``("navigator℘claude", "claude")`` —
65
+ the persona (left) and harness (right) are validated and re-joined with
66
+ the canonical ``℘`` separator to form the node-name.
67
+
68
+ Splits on the FIRST separator only; a second separator in EITHER segment is
69
+ rejected (a segment must be fs/key-safe: alnum plus ``-``/``.``/``_``).
70
+
71
+ :raises ConfigError: on empty input, an empty persona/harness segment, or a
72
+ segment containing an illegal character (incl. a stray separator).
73
+ """
74
+ if not isinstance(raw, str):
75
+ raise ConfigError(
76
+ f"agent ref must be a string, got {type(raw).__name__}: {raw!r}"
77
+ )
78
+ ref = raw.strip()
79
+ if not ref:
80
+ raise ConfigError("agent ref is empty")
81
+
82
+ idx = _first_sep_index(ref)
83
+ if idx == -1:
84
+ # Bare: node == harness == the whole (validated) name.
85
+ if not _is_segment_safe(ref):
86
+ raise ConfigError(
87
+ f"invalid agent name '{ref}': names may contain only letters, "
88
+ f"digits, '-', '.', '_'"
89
+ )
90
+ return ref, ref
91
+
92
+ persona = ref[:idx]
93
+ harness = ref[idx + 1 :]
94
+
95
+ if not _is_segment_safe(persona):
96
+ raise ConfigError(
97
+ f"invalid agent ref '{raw}': the persona segment '{persona}' must be "
98
+ f"non-empty and contain only letters, digits, '-', '.', '_' "
99
+ f"(no separator)"
100
+ )
101
+ if not _is_segment_safe(harness):
102
+ raise ConfigError(
103
+ f"invalid agent ref '{raw}': the harness segment '{harness}' must be "
104
+ f"non-empty and contain only letters, digits, '-', '.', '_' "
105
+ f"(no separator)"
106
+ )
107
+
108
+ node = f"{persona}{CANONICAL_SEP}{harness}"
109
+ return node, harness
110
+
111
+
112
+ def harness_of(node: str) -> str:
113
+ """Return the harness (part right of ``℘``) of a *node*-name.
114
+
115
+ A bare node with no separator IS its own harness (``"claude" -> "claude"``).
116
+ Only the canonical ``℘`` is recognised here — callers pass node-names, which
117
+ are always canonicalised. ``+`` is deliberately NOT split here so a literal
118
+ ``+`` in an already-canonical node (there should be none) is not mistaken for
119
+ a separator; use :func:`parse_agent_ref` / :func:`canonicalize_agent_ref` for
120
+ raw, possibly-``+`` input.
121
+ """
122
+ _, _, harness = node.rpartition(CANONICAL_SEP)
123
+ return harness if harness else node
124
+
125
+
126
+ def with_harness(node: str, harness: str) -> str:
127
+ """Return *node* with its harness segment REPLACED by *harness*.
128
+
129
+ Preserves the persona (left of ``℘``) while swapping the harness — used when
130
+ the actually-RESOLVED target differs from the requested harness (e.g. the
131
+ ``NoAgentTarget`` fallback when a named agent's binary is absent), so the
132
+ on-disk ``agents/<node>/`` dir + keyspace slot follow the real target.
133
+
134
+ * bare node (``"claude"``, harness ``"claude"``) -> ``"claude"`` (unchanged);
135
+ * bare node, fallback harness (``"claude"``, ``"no_agent"``) -> ``"no_agent"``;
136
+ * rider node (``"navigator℘claude"``, ``"claude"``) -> ``"navigator℘claude"``;
137
+ * rider node, fallback (``"navigator℘claude"``, ``"no_agent"``) ->
138
+ ``"navigator℘no_agent"`` (persona kept, harness swapped).
139
+ """
140
+ persona, sep, _ = node.rpartition(CANONICAL_SEP)
141
+ if not sep:
142
+ # Bare node: it IS just a harness — replace wholesale.
143
+ return harness
144
+ return f"{persona}{CANONICAL_SEP}{harness}"
145
+
146
+
147
+ def display_agent_ref(node: str) -> str:
148
+ """Return the USER-FACING form of a *node*-name (``℘`` -> ``+``).
149
+
150
+ Presentation-only inverse of :func:`canonicalize_agent_ref`: internal storage,
151
+ keyspace, on-disk paths, and ``KANIBAKO_AGENT`` all keep the canonical ``℘``
152
+ form; whenever an agent ref is rendered to the user (error text, ``crab``/``box``
153
+ listings, session labels, log lines) it is shown with the typable ``+``.
154
+
155
+ Bare names contain no ``℘`` -> returned unchanged (existing output byte-identical).
156
+ Does NOT validate: it is a pure display swap tolerant of any string.
157
+ """
158
+ return node.replace(CANONICAL_SEP, PLUS_SEP)
159
+
160
+
161
+ def canonicalize_agent_ref(raw: str) -> str:
162
+ """Return the canonical *node*-name for a raw ref (``+`` -> ``℘``).
163
+
164
+ Idempotent: a node that is already canonical (or bare) returns unchanged.
165
+ Validates via :func:`parse_agent_ref`, so a malformed ref raises ``ConfigError``.
166
+ """
167
+ node, _ = parse_agent_ref(raw)
168
+ return node
@@ -88,17 +88,29 @@ if TYPE_CHECKING:
88
88
  def agent_default_partial(
89
89
  descriptor: PluginDescriptor,
90
90
  install: AgentInstall,
91
+ node_name: str | None = None,
91
92
  ) -> KeyStore:
92
93
  """Represent a descriptor's delivery bindings as an agent-level KeyStore partial.
93
94
 
94
95
  Returns a :class:`~kanibako.settings_store.KeyStore` partial rooted at
95
- ``agent.<name>`` where ``<name>`` is the descriptor's OWN agent (``install.name``;
96
- S27 / §2d), holding each resolvable :class:`~kanibako.targets.base.Binding` as a
97
- ``agent.<name>.bindings.{ro,rw}.<key> = Bind(host_src, box_dest, opts)`` leaf —
96
+ ``agent.<node>`` where ``<node>`` is the ACTIVE node-name (*node_name*), holding
97
+ each resolvable :class:`~kanibako.targets.base.Binding` as a
98
+ ``agent.<node>.bindings.{ro,rw}.<key> = Bind(host_src, box_dest, opts)`` leaf —
98
99
  mirroring :func:`~kanibako.targets.assembly.resolve_binding_source` with NO
99
100
  override and NO existence check (S26). See the module docstring for the full
100
101
  rules and the None-origin OMIT contract.
101
102
 
103
+ RIDER threading (Block E fix 2a): the read side (``_agent_pick_node``) walks
104
+ ``agent.default`` ∪ ``agent.<active_agent>``, where ``<active_agent>`` is the
105
+ resolved NODE-name (``navigator℘claude`` for a rider). The descriptor's
106
+ ``install.name`` is the HARNESS (``"claude"``, hardcoded in claude's ``detect()``),
107
+ so rooting the binds under ``install.name`` ORPHANS a rider's AGENT_CRITICAL
108
+ delivery binds at ``agent.claude.*`` (never read → the ``claude`` binary is never
109
+ mounted → the container exits immediately). So the partial roots under the ACTIVE
110
+ node-name (*node_name*). For a BARE agent node==harness=="claude", so the binds
111
+ still land at ``agent.claude.*`` — byte-identical. *node_name* falls back to
112
+ ``install.name`` only when a caller omits it (legacy / test convenience).
113
+
102
114
  The partial nests ``agent.<name>.bindings`` with ``ro`` / ``rw`` sub-tables; a
103
115
  sub-table is present only if at least one binding lands in it, ``bindings`` is
104
116
  present only if at least one binding resolved, and the result is ALWAYS a
@@ -134,12 +146,14 @@ def agent_default_partial(
134
146
  if dict.__len__(rw_binds):
135
147
  bindings["rw"] = rw_binds
136
148
 
137
- # Root under the descriptor's OWN agent name (§2d ``agent.<agent>.*``): the
138
- # claude descriptor's binds land at ``agent.claude.bindings.*``. The agent NAME
139
- # is part of the KEY PATH (NOT a bare ``agent`` token, §0 L21) so this partial
140
- # merges BY NAME with 2a's discriminated ``agent.<active>.*`` level and any
141
- # higher-scope ``agent.<name>.*`` override (block 2b).
142
- name = install.name
149
+ # Root under the ACTIVE node-name (§2d ``agent.<agent>.*``): for a BARE agent the
150
+ # node-name IS the harness (``agent.claude.bindings.*``); for a RIDER it is the
151
+ # composite node (``agent.navigator℘claude.bindings.*``) the read side actually
152
+ # walks (fix 2a). The agent NAME is part of the KEY PATH (NOT a bare ``agent``
153
+ # token, §0 L21) so this partial merges BY NAME with 2a's discriminated
154
+ # ``agent.<active>.*`` level and any higher-scope ``agent.<node>.*`` override
155
+ # (block 2b), including the node-keyed override bridge.
156
+ name = node_name if node_name is not None else install.name
143
157
  agent_sub = KeyStore()
144
158
  if dict.__len__(bindings):
145
159
  agent_sub["bindings"] = bindings
@@ -9,17 +9,17 @@ file-seeding live in sub-step 6b.
9
9
 
10
10
  Two channel scopes (TARGET §1, §2c, §2f):
11
11
 
12
- * **system scope** — five type roots under ``@system.channels`` (commons, chat,
12
+ * **system scope** — five type roots under ``@system.channelroot`` (commons, chat,
13
13
  broadcast, mailboxes, share). The instance-owned types (mailboxes, share) are
14
14
  *partitioned* by the workset-name token: ``mailboxes/<ws>`` and ``share/<ws>``
15
15
  where ``<ws>`` is ``__PRIMARY__`` | ``<named>`` | ``__STANDALONE__``. These
16
16
  partition roots apply to EVERY mode (standalone included).
17
- * **workset scope** — three type roots under ``@workset.meta.root/channels``
17
+ * **workset scope** — three type roots under ``@meta.workset.path/channels``
18
18
  (commons, chat, share). These exist for the PRIMARY and NAMED modes ONLY;
19
- standalone has no workset-local channels (its ``workset.channels`` is
19
+ standalone has no workset-local channels (its ``workset.channelroot`` is
20
20
  ``<None>`` per the TARGET).
21
21
 
22
- The per-instance partition ADDRESSES (``box.meta.{inbox,share_global,
22
+ The per-instance partition ADDRESSES (``meta.box.{inbox,share_global,
23
23
  share_workset}``) are this box's own mailbox/share dirs *within* the partitioned
24
24
  roots — derived from the workset-name token + the box name.
25
25
 
@@ -27,7 +27,7 @@ roots — derived from the workset-name token + the box name.
27
27
  ``ProjectPaths`` (the legacy comms block used only ``proj.name``). Per the
28
28
  design-review A8 resolution we derive them HERE from ``proj.mode`` + ``proj.
29
29
  group`` rather than widening the resolver's public shape: PRIMARY roots at
30
- ``@system.primary_workset`` with token ``__PRIMARY__``; NAMED roots at
30
+ ``@config.primary_workset`` with token ``__PRIMARY__``; NAMED roots at
31
31
  ``proj.group.root`` with token ``proj.group.name``; STANDALONE roots at
32
32
  ``proj.metadata_path`` (the root; the workspace is a subdir) with token
33
33
  ``__STANDALONE__``.
@@ -68,9 +68,9 @@ class SystemPartition:
68
68
  class WorksetChannels:
69
69
  """The workset-local channel roots (PRIMARY/NAMED only).
70
70
 
71
- ``@workset.channels = @workset.meta.root/channels`` with ``commons``,
71
+ ``@workset.channelroot = @meta.workset.path/channels`` with ``commons``,
72
72
  ``chat`` (+ the reserved ``broadcast.md`` / default ``general.md`` files
73
- inside it), and ``share`` (per-box subdirs are ``box.meta.share_workset``).
73
+ inside it), and ``share`` (per-box subdirs are ``meta.box.share_workset``).
74
74
  """
75
75
 
76
76
  root: Path
@@ -83,7 +83,7 @@ class WorksetChannels:
83
83
 
84
84
  @dataclass(frozen=True)
85
85
  class BoxChannelAddresses:
86
- """This box's own partition ADDRESSES (TARGET §2c ``box.meta.*``).
86
+ """This box's own partition ADDRESSES (TARGET §2c ``meta.box.*``).
87
87
 
88
88
  * ``inbox`` == ``@system.channels.mailboxes/<ws>/<box>`` — this box's own
89
89
  mailbox dir (also surfaced in-box at ``~/channels/inbox``).
@@ -126,7 +126,7 @@ def own_partition_dirs(
126
126
  The lower-level primitive behind :func:`box_channel_addresses` — it takes the
127
127
  workset-name token + box name directly (no ``ProjectPaths``), so the lifecycle
128
128
  relocation can compute BOTH the OLD and the NEW partition for a box being
129
- moved/converted. Mirrors :func:`system_partition` + the ``box.meta.{inbox,
129
+ moved/converted. Mirrors :func:`system_partition` + the ``meta.box.{inbox,
130
130
  share_global}`` joins (TARGET §2c).
131
131
  """
132
132
  part = system_partition(std, ws_token)
@@ -162,9 +162,9 @@ def workset_name_token(proj: ProjectPaths) -> str:
162
162
 
163
163
 
164
164
  def workset_root(proj: ProjectPaths, std: StandardPaths) -> Path:
165
- """Return ``@workset.meta.root`` for *proj*.
165
+ """Return ``@meta.workset.path`` for *proj*.
166
166
 
167
- PRIMARY → ``@system.primary_workset``; NAMED → the named workset root
167
+ PRIMARY → ``@config.primary_workset``; NAMED → the named workset root
168
168
  (``proj.group.root``); STANDALONE → the project root itself
169
169
  (``proj.metadata_path``, which for standalone IS the root — the workspace is
170
170
  a ``workspace/`` subdir under it, so ``project_path`` is not the root).
@@ -214,7 +214,7 @@ def workset_channel_paths(
214
214
  """Derive the WORKSET-local channel roots for *proj* (PRIMARY/NAMED only).
215
215
 
216
216
  Returns ``None`` for standalone (no workset-local channels). Rooted at
217
- ``@workset.meta.root/channels`` (A3 — a derived helper, NOT new fields on
217
+ ``@meta.workset.path/channels`` (A3 — a derived helper, NOT new fields on
218
218
  ``ProjectPaths``).
219
219
  """
220
220
  if not has_workset_channels(proj):
@@ -234,7 +234,7 @@ def workset_channel_paths(
234
234
  def box_channel_addresses(
235
235
  proj: ProjectPaths, std: StandardPaths
236
236
  ) -> BoxChannelAddresses:
237
- """Derive this box's own partition addresses (``box.meta.*``) for *proj*.
237
+ """Derive this box's own partition addresses (``meta.box.*``) for *proj*.
238
238
 
239
239
  ``inbox`` / ``share_global`` always resolve (system-scope, every mode);
240
240
  ``share_workset`` is ``None`` for standalone. *proj.name* is the box name.