worktree-runtime 0.1.0a3__tar.gz → 0.1.0a4__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 (99) hide show
  1. {worktree_runtime-0.1.0a3 → worktree_runtime-0.1.0a4}/Cargo.lock +3 -3
  2. {worktree_runtime-0.1.0a3 → worktree_runtime-0.1.0a4}/Cargo.toml +3 -3
  3. {worktree_runtime-0.1.0a3 → worktree_runtime-0.1.0a4}/LICENSE +7 -1
  4. worktree_runtime-0.1.0a4/PKG-INFO +127 -0
  5. worktree_runtime-0.1.0a4/README.md +102 -0
  6. worktree_runtime-0.1.0a4/crates/worktree_runtime_core/AGENTS.md +50 -0
  7. {worktree_runtime-0.1.0a3 → worktree_runtime-0.1.0a4}/crates/worktree_runtime_core/src/application/binding_lifecycle.rs +158 -82
  8. worktree_runtime-0.1.0a4/crates/worktree_runtime_core/src/application/git_worktree_service.rs +126 -0
  9. {worktree_runtime-0.1.0a3 → worktree_runtime-0.1.0a4}/crates/worktree_runtime_core/src/application/validation.rs +39 -9
  10. worktree_runtime-0.1.0a4/crates/worktree_runtime_core/src/application.rs +4 -0
  11. worktree_runtime-0.1.0a3/crates/worktree_runtime_core/src/infrastructure/audit_record.rs → worktree_runtime-0.1.0a4/crates/worktree_runtime_core/src/domain/audit.rs +28 -49
  12. {worktree_runtime-0.1.0a3 → worktree_runtime-0.1.0a4}/crates/worktree_runtime_core/src/domain/paths.rs +13 -1
  13. worktree_runtime-0.1.0a4/crates/worktree_runtime_core/src/domain/porcelain.rs +132 -0
  14. worktree_runtime-0.1.0a4/crates/worktree_runtime_core/src/domain.rs +7 -0
  15. worktree_runtime-0.1.0a4/crates/worktree_runtime_core/src/infrastructure/audit_dir.rs +38 -0
  16. {worktree_runtime-0.1.0a3 → worktree_runtime-0.1.0a4}/crates/worktree_runtime_core/src/infrastructure/audit_file_io.rs +82 -6
  17. worktree_runtime-0.1.0a4/crates/worktree_runtime_core/src/infrastructure/audit_reader.rs +80 -0
  18. worktree_runtime-0.1.0a4/crates/worktree_runtime_core/src/infrastructure/audit_writer.rs +135 -0
  19. worktree_runtime-0.1.0a4/crates/worktree_runtime_core/src/infrastructure/binding_store.rs +290 -0
  20. worktree_runtime-0.1.0a4/crates/worktree_runtime_core/src/infrastructure/git_adapter.rs +190 -0
  21. worktree_runtime-0.1.0a4/crates/worktree_runtime_core/src/infrastructure/github_policy_loader.rs +88 -0
  22. worktree_runtime-0.1.0a4/crates/worktree_runtime_core/src/infrastructure/project_id_resolver.rs +133 -0
  23. {worktree_runtime-0.1.0a3 → worktree_runtime-0.1.0a4}/crates/worktree_runtime_core/src/infrastructure/worktree_base_resolver.rs +5 -38
  24. {worktree_runtime-0.1.0a3 → worktree_runtime-0.1.0a4}/crates/worktree_runtime_core/src/infrastructure/worktree_config_loader.rs +16 -6
  25. worktree_runtime-0.1.0a4/crates/worktree_runtime_core/src/infrastructure/worktree_list_reader.rs +75 -0
  26. {worktree_runtime-0.1.0a3 → worktree_runtime-0.1.0a4}/crates/worktree_runtime_core/src/infrastructure.rs +3 -1
  27. {worktree_runtime-0.1.0a3 → worktree_runtime-0.1.0a4}/crates/worktree_runtime_core/tests/common/mod.rs +103 -5
  28. {worktree_runtime-0.1.0a3/crates/worktree_runtime_core/tests → worktree_runtime-0.1.0a4/crates/worktree_runtime_core/tests/e2e}/binding_lifecycle_tests.rs +113 -38
  29. worktree_runtime-0.1.0a4/crates/worktree_runtime_core/tests/e2e/git_worktree_service_tests.rs +215 -0
  30. worktree_runtime-0.1.0a4/crates/worktree_runtime_core/tests/e2e/main.rs +8 -0
  31. {worktree_runtime-0.1.0a3/crates/worktree_runtime_core/tests → worktree_runtime-0.1.0a4/crates/worktree_runtime_core/tests/e2e}/validation_tests.rs +51 -7
  32. worktree_runtime-0.1.0a4/crates/worktree_runtime_core/tests/integration/audit_dir_tests.rs +84 -0
  33. worktree_runtime-0.1.0a4/crates/worktree_runtime_core/tests/integration/audit_file_io_tests.rs +169 -0
  34. worktree_runtime-0.1.0a4/crates/worktree_runtime_core/tests/integration/audit_reader_tests.rs +108 -0
  35. {worktree_runtime-0.1.0a3/crates/worktree_runtime_core/tests → worktree_runtime-0.1.0a4/crates/worktree_runtime_core/tests/integration}/audit_writer_tests.rs +190 -213
  36. worktree_runtime-0.1.0a4/crates/worktree_runtime_core/tests/integration/binding_store_tests.rs +354 -0
  37. worktree_runtime-0.1.0a4/crates/worktree_runtime_core/tests/integration/git_adapter_tests.rs +73 -0
  38. worktree_runtime-0.1.0a4/crates/worktree_runtime_core/tests/integration/github_policy_loader_tests.rs +177 -0
  39. worktree_runtime-0.1.0a4/crates/worktree_runtime_core/tests/integration/main.rs +17 -0
  40. worktree_runtime-0.1.0a4/crates/worktree_runtime_core/tests/integration/path_resolution_tests.rs +17 -0
  41. {worktree_runtime-0.1.0a3/crates/worktree_runtime_core/tests → worktree_runtime-0.1.0a4/crates/worktree_runtime_core/tests/integration}/project_id_resolver_tests.rs +79 -176
  42. {worktree_runtime-0.1.0a3/crates/worktree_runtime_core/tests → worktree_runtime-0.1.0a4/crates/worktree_runtime_core/tests/integration}/worktree_base_resolver_tests.rs +59 -29
  43. {worktree_runtime-0.1.0a3/crates/worktree_runtime_core/tests → worktree_runtime-0.1.0a4/crates/worktree_runtime_core/tests/integration}/worktree_config_loader_tests.rs +39 -7
  44. {worktree_runtime-0.1.0a3/crates/worktree_runtime_core/tests → worktree_runtime-0.1.0a4/crates/worktree_runtime_core/tests/integration}/worktree_list_reader_tests.rs +42 -86
  45. worktree_runtime-0.1.0a4/crates/worktree_runtime_core/tests/unit/audit_tests.rs +125 -0
  46. {worktree_runtime-0.1.0a3/crates/worktree_runtime_core/tests → worktree_runtime-0.1.0a4/crates/worktree_runtime_core/tests/unit}/binding_tests.rs +9 -3
  47. {worktree_runtime-0.1.0a3/crates/worktree_runtime_core/tests → worktree_runtime-0.1.0a4/crates/worktree_runtime_core/tests/unit}/env_keys_tests.rs +4 -3
  48. {worktree_runtime-0.1.0a3/crates/worktree_runtime_core/tests → worktree_runtime-0.1.0a4/crates/worktree_runtime_core/tests/unit}/git_worktree_tests.rs +7 -2
  49. worktree_runtime-0.1.0a4/crates/worktree_runtime_core/tests/unit/github_policy_tests.rs +31 -0
  50. worktree_runtime-0.1.0a4/crates/worktree_runtime_core/tests/unit/main.rs +9 -0
  51. {worktree_runtime-0.1.0a3/crates/worktree_runtime_core/tests → worktree_runtime-0.1.0a4/crates/worktree_runtime_core/tests/unit}/paths_tests.rs +33 -3
  52. worktree_runtime-0.1.0a4/crates/worktree_runtime_core/tests/unit/porcelain_tests.rs +82 -0
  53. worktree_runtime-0.1.0a4/crates/worktree_runtime_pyo3/src/convert.rs +148 -0
  54. worktree_runtime-0.1.0a4/crates/worktree_runtime_pyo3/src/functions.rs +446 -0
  55. worktree_runtime-0.1.0a4/crates/worktree_runtime_pyo3/src/lib.rs +134 -0
  56. worktree_runtime-0.1.0a4/crates/worktree_runtime_pyo3/src/types.rs +563 -0
  57. worktree_runtime-0.1.0a4/crates/worktree_runtime_pyo3/tests/pyo3_signature_freeze_tests.rs +173 -0
  58. {worktree_runtime-0.1.0a3 → worktree_runtime-0.1.0a4}/pyproject.toml +19 -2
  59. worktree_runtime-0.1.0a4/src/worktree_runtime/AGENTS.md +21 -0
  60. {worktree_runtime-0.1.0a3 → worktree_runtime-0.1.0a4}/src/worktree_runtime/SURFACE.yaml +23 -0
  61. {worktree_runtime-0.1.0a3 → worktree_runtime-0.1.0a4}/src/worktree_runtime/api/__init__.py +16 -35
  62. worktree_runtime-0.1.0a4/src/worktree_runtime/api/__init__.pyi +356 -0
  63. worktree_runtime-0.1.0a4/src/worktree_runtime/presentation/audit_cli.py +91 -0
  64. worktree_runtime-0.1.0a3/PKG-INFO +0 -9
  65. worktree_runtime-0.1.0a3/crates/worktree_runtime_core/AGENTS.md +0 -9
  66. worktree_runtime-0.1.0a3/crates/worktree_runtime_core/src/application/audit_service.rs +0 -7
  67. worktree_runtime-0.1.0a3/crates/worktree_runtime_core/src/application/checkout_service.rs +0 -97
  68. worktree_runtime-0.1.0a3/crates/worktree_runtime_core/src/application.rs +0 -5
  69. worktree_runtime-0.1.0a3/crates/worktree_runtime_core/src/domain/porcelain.rs +0 -35
  70. worktree_runtime-0.1.0a3/crates/worktree_runtime_core/src/domain.rs +0 -6
  71. worktree_runtime-0.1.0a3/crates/worktree_runtime_core/src/infrastructure/audit_writer.rs +0 -184
  72. worktree_runtime-0.1.0a3/crates/worktree_runtime_core/src/infrastructure/binding_store.rs +0 -139
  73. worktree_runtime-0.1.0a3/crates/worktree_runtime_core/src/infrastructure/git_adapter.rs +0 -57
  74. worktree_runtime-0.1.0a3/crates/worktree_runtime_core/src/infrastructure/project_id_resolver.rs +0 -190
  75. worktree_runtime-0.1.0a3/crates/worktree_runtime_core/src/infrastructure/worktree_list_reader.rs +0 -178
  76. worktree_runtime-0.1.0a3/crates/worktree_runtime_core/tests/audit_file_io_tests.rs +0 -103
  77. worktree_runtime-0.1.0a3/crates/worktree_runtime_core/tests/audit_record_tests.rs +0 -45
  78. worktree_runtime-0.1.0a3/crates/worktree_runtime_core/tests/audit_service_tests.rs +0 -44
  79. worktree_runtime-0.1.0a3/crates/worktree_runtime_core/tests/binding_store_tests.rs +0 -215
  80. worktree_runtime-0.1.0a3/crates/worktree_runtime_core/tests/checkout_service_tests.rs +0 -124
  81. worktree_runtime-0.1.0a3/crates/worktree_runtime_pyo3/src/lib.rs +0 -1075
  82. worktree_runtime-0.1.0a3/crates/worktree_runtime_pyo3/tests/pyo3_signature_freeze_tests.rs +0 -31
  83. worktree_runtime-0.1.0a3/src/worktree_runtime/AGENTS.md +0 -14
  84. worktree_runtime-0.1.0a3/src/worktree_runtime/api/__init__.pyi +0 -144
  85. worktree_runtime-0.1.0a3/src/worktree_runtime/api.pyi +0 -144
  86. worktree_runtime-0.1.0a3/src/worktree_runtime/presentation/audit_cli.py +0 -68
  87. {worktree_runtime-0.1.0a3 → worktree_runtime-0.1.0a4}/crates/worktree_runtime_core/Cargo.toml +0 -0
  88. {worktree_runtime-0.1.0a3 → worktree_runtime-0.1.0a4}/crates/worktree_runtime_core/src/domain/binding.rs +0 -0
  89. {worktree_runtime-0.1.0a3 → worktree_runtime-0.1.0a4}/crates/worktree_runtime_core/src/domain/git_worktree.rs +0 -0
  90. {worktree_runtime-0.1.0a3 → worktree_runtime-0.1.0a4}/crates/worktree_runtime_core/src/domain/github_policy.rs +0 -0
  91. {worktree_runtime-0.1.0a3 → worktree_runtime-0.1.0a4}/crates/worktree_runtime_core/src/infrastructure/env_keys.rs +0 -0
  92. {worktree_runtime-0.1.0a3 → worktree_runtime-0.1.0a4}/crates/worktree_runtime_core/src/infrastructure/path_resolution.rs +0 -0
  93. {worktree_runtime-0.1.0a3 → worktree_runtime-0.1.0a4}/crates/worktree_runtime_core/src/lib.rs +0 -0
  94. {worktree_runtime-0.1.0a3 → worktree_runtime-0.1.0a4}/crates/worktree_runtime_pyo3/AGENTS.md +0 -0
  95. {worktree_runtime-0.1.0a3 → worktree_runtime-0.1.0a4}/crates/worktree_runtime_pyo3/Cargo.toml +0 -0
  96. {worktree_runtime-0.1.0a3 → worktree_runtime-0.1.0a4}/src/_worktree_runtime_rust/__init__.py +0 -0
  97. {worktree_runtime-0.1.0a3 → worktree_runtime-0.1.0a4}/src/worktree_runtime/__init__.py +0 -0
  98. {worktree_runtime-0.1.0a3 → worktree_runtime-0.1.0a4}/src/worktree_runtime/presentation/__init__.py +0 -0
  99. {worktree_runtime-0.1.0a3 → worktree_runtime-0.1.0a4}/src/worktree_runtime/py.typed +0 -0
@@ -1,6 +1,6 @@
1
1
  # This file is automatically @generated by Cargo.
2
2
  # It is not intended for manual editing.
3
- version = 3
3
+ version = 4
4
4
 
5
5
  [[package]]
6
6
  name = "android_system_properties"
@@ -743,7 +743,7 @@ dependencies = [
743
743
 
744
744
  [[package]]
745
745
  name = "worktree_runtime_core"
746
- version = "0.1.0-alpha.3"
746
+ version = "0.1.0-alpha.4"
747
747
  dependencies = [
748
748
  "chrono",
749
749
  "fs2",
@@ -760,7 +760,7 @@ dependencies = [
760
760
 
761
761
  [[package]]
762
762
  name = "worktree_runtime_pyo3"
763
- version = "0.1.0-alpha.3"
763
+ version = "0.1.0-alpha.4"
764
764
  dependencies = [
765
765
  "chrono",
766
766
  "pyo3",
@@ -3,9 +3,9 @@ resolver = "2"
3
3
  members = ["crates/worktree_runtime_core", "crates/worktree_runtime_pyo3"]
4
4
 
5
5
  [workspace.package]
6
- version = "0.1.0-alpha.3"
6
+ version = "0.1.0-alpha.4"
7
7
  edition = "2021"
8
- rust-version = "1.75"
8
+ rust-version = "1.85"
9
9
  license = "LicenseRef-Proprietary"
10
10
  publish = false
11
11
 
@@ -18,7 +18,7 @@ sha1 = "0.10"
18
18
  fs2 = "0.4"
19
19
  tempfile = "3.10"
20
20
  wait-timeout = "0.2"
21
- pyo3 = { version = "0.29", features = ["extension-module"] }
21
+ pyo3 = { version = "0.29", features = ["extension-module", "abi3-py310"] }
22
22
  chrono = "0.4"
23
23
 
24
24
  [profile.release]
@@ -1,4 +1,4 @@
1
- Copyright (c) 2026 adaptive-task contributors
1
+ Copyright (c) 2026 bamboocity
2
2
  All rights reserved.
3
3
 
4
4
  This software and associated documentation files (the "Software") are
@@ -6,6 +6,12 @@ proprietary. Unauthorized copying, modification, distribution, or use of
6
6
  the Software, via any medium, is strictly prohibited except as expressly
7
7
  authorized in writing by the copyright holder.
8
8
 
9
+ The Software is distributed on the Python Package Index as binary wheels
10
+ and as a source distribution (sdist) that contains this source code. That
11
+ distribution is provided solely so the package can be installed and built;
12
+ it does not grant any right to copy, modify, or redistribute the source
13
+ beyond what is necessary to install the Software.
14
+
9
15
  THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
10
16
  IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
11
17
  FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
@@ -0,0 +1,127 @@
1
+ Metadata-Version: 2.4
2
+ Name: worktree-runtime
3
+ Version: 0.1.0a4
4
+ Classifier: Development Status :: 3 - Alpha
5
+ Classifier: License :: Other/Proprietary License
6
+ Classifier: Operating System :: MacOS
7
+ Classifier: Operating System :: Microsoft :: Windows
8
+ Classifier: Operating System :: POSIX :: Linux
9
+ Classifier: Programming Language :: Python :: 3
10
+ Classifier: Programming Language :: Python :: 3 :: Only
11
+ Classifier: Programming Language :: Rust
12
+ Classifier: Typing :: Typed
13
+ Requires-Dist: pytest>=8.0 ; extra == 'dev'
14
+ Requires-Dist: pyyaml>=6.0 ; extra == 'dev'
15
+ Requires-Dist: mypy>=1.14 ; extra == 'dev'
16
+ Provides-Extra: dev
17
+ License-File: LICENSE
18
+ Summary: Worktree identity, binding persistence, and checkout substrate
19
+ Author: bamboocity
20
+ Requires-Python: >=3.10
21
+ Description-Content-Type: text/markdown; charset=UTF-8; variant=GFM
22
+ Project-URL: Changelog, https://github.com/bamboocity/worktree-runtime/blob/main/CHANGELOG.md
23
+ Project-URL: Repository, https://github.com/bamboocity/worktree-runtime
24
+
25
+ # worktree-runtime
26
+
27
+ Library for worktree **identity**, **binding persistence**, **checkout
28
+ validation**, and **path resolution**.
29
+
30
+ Consumers (adaptive-task / Noveler) call this so they do not each invent
31
+ project id, session↔worktree binding, main-vs-linked checks, or
32
+ state / shared paths. The public symbol list is
33
+ [`src/worktree_runtime/SURFACE.yaml`](src/worktree_runtime/SURFACE.yaml).
34
+
35
+ Binding store API: `register_binding`, `load_binding`, `recover_binding`,
36
+ `list_bindings`, and `remove_binding`. `git worktree remove` and vanish stay
37
+ in the consumer.
38
+
39
+ Rust is the implementation SSOT. Python is a shim, typing stubs, contract tests,
40
+ and a thin audit CLI. Missing native support is an install error; there is no
41
+ silent Python fallback in this project.
42
+
43
+ ## Scope
44
+
45
+ This crate answers four questions and records consumer mutations:
46
+
47
+ | Responsibility | What the library does |
48
+ | --- | --- |
49
+ | Identity | Resolve project id, main branches, GitHub policy, and the `NOVELER_*` / location env catalog from cwd, env, git, and project config. |
50
+ | Binding | Persist, load, recover, and validate a session↔worktree record (`WorktreeBinding`). Reject before treating a cwd as bound. |
51
+ | Checkout | Resolve a `GitWorktree` (`rev-parse`), classify main vs linked, list porcelain entries, emit reason codes. |
52
+ | Path | Resolve worktree base / dir / state dir / known bases with fixed precedence. `NOVELER_WORKTREE_BASE` and `config/worktree.yaml` overrides apply only when `CI` / `GITHUB_ACTIONS` is set; elsewhere the base is `~/.adaptive-task/worktrees/<project_id>`. Normalize and fingerprint paths. |
53
+
54
+ Audit (`record_event` / `read_records` / rotation, plus
55
+ `presentation/audit_cli.py`) is append-only logging of consumer git
56
+ mutations (`worktree_remove`, `branch_delete`). It is not a session log.
57
+
58
+ ### Out of scope
59
+
60
+ - **Agent isolation.** Codex Desktop, Cursor, and Claude Code create a
61
+ worktree (or checkout) so one chat does not dirty the user's main tree.
62
+ That sandbox is a different layer. This package does not replace it.
63
+ - **Session orchestration.** `adaptive-task init` / `next` / `report` stay
64
+ in the consumer.
65
+ - **Prep / vanish / cleanup hooks.** Detecting a disappeared worktree,
66
+ refusing cleanup, and running merge-cleanup tooling stay in adaptive-task.
67
+ - **`git worktree add` / `remove` as a product operation.** This crate
68
+ resolves locations and validates state. The consumer mutates git.
69
+
70
+ Using Codex Desktop only as an editor does not require importing this
71
+ package. Running adaptive-task identity, binding, or path policy still does.
72
+ Agent isolation worktrees and session worktrees can nest. Config may move
73
+ the session base when `CODEX_CLI` or `CLAUDE_CODE` is set. That override
74
+ exists because the two layers meet; it does not make this library redundant.
75
+
76
+ ## Architecture
77
+
78
+ ```mermaid
79
+ flowchart LR
80
+ callers["consumers"] -->|"worktree_runtime.api"| shim["Python shim and stubs"]
81
+ shim --> pyo3["PyO3 binding"]
82
+ pyo3 --> core["Rust core"]
83
+ contract["contract pytest"] -->|"black-box"| shim
84
+ cargo["cargo test"] --> core
85
+ release["maturin wheels"] --> callers
86
+ ```
87
+
88
+ ## Layout
89
+
90
+ Layout rationale is in [DESIGN.md](DESIGN.md).
91
+
92
+ - `crates/worktree_runtime_core/` — behavior SSOT
93
+ - `crates/worktree_runtime_pyo3/` — Python C-API translation only
94
+ - `src/worktree_runtime/` — `api` re-export, `.pyi`, `SURFACE.yaml`, `presentation/audit_cli.py`
95
+ - `src/_worktree_runtime_rust/` — placeholder package that hosts the cdylib built by maturin (`python-source = "src"`)
96
+ - `tests/contract/` — `from worktree_runtime.api import ...` only; `tests/cli/` — audit CLI exit codes
97
+
98
+ Distribution name is `worktree-runtime`. Import name is `worktree_runtime`.
99
+ `pyproject.toml` version and `Cargo.toml` `workspace.package.version` must match.
100
+ The crate is proprietary (`LICENSE`; all rights reserved). CI uses the `stable` toolchain with rustfmt and clippy.
101
+
102
+ ## Change protocol
103
+
104
+ Multi-file, refactor, behavior, or core+pyo3+facade+contract work: run
105
+ `adaptive-gate` first ([AGENTS.md](AGENTS.md) Routing). Testing policy:
106
+ `.cursor/rules/testing.mdc`.
107
+
108
+ 1. Behavior: add a failing cargo execution test, then implement in
109
+ `worktree_runtime_core` and run `cargo test`.
110
+ 2. Public-surface shape only: update `SURFACE.yaml` and `.pyi`, plus a thin
111
+ contract assertion under `tests/contract/`. Do not put behavior suites in
112
+ pytest.
113
+ 3. Export through PyO3; keep the shim as a re-export.
114
+ 4. When the public surface changed, build a wheel and run the contract and
115
+ CLI pytest. `cargo fmt --check` and `clippy -D warnings` are required.
116
+
117
+ The verify loop (commands, hook setup, release steps) is defined once in
118
+ [BUILD.md](BUILD.md).
119
+
120
+ ## Build
121
+
122
+ See [BUILD.md](BUILD.md). CI always runs fmt, clippy, cargo test, maturin,
123
+ contract+CLI pytest, `mypy.stubtest`, and an MSRV check, through
124
+ `.github/actions/rust-checks`.
125
+ Wheels are `abi3-py310` (CPython 3.10+) for Windows amd64, macOS x86_64 and
126
+ arm64, and manylinux x86_64 / aarch64. Pure-Python wheels are not published.
127
+
@@ -0,0 +1,102 @@
1
+ # worktree-runtime
2
+
3
+ Library for worktree **identity**, **binding persistence**, **checkout
4
+ validation**, and **path resolution**.
5
+
6
+ Consumers (adaptive-task / Noveler) call this so they do not each invent
7
+ project id, session↔worktree binding, main-vs-linked checks, or
8
+ state / shared paths. The public symbol list is
9
+ [`src/worktree_runtime/SURFACE.yaml`](src/worktree_runtime/SURFACE.yaml).
10
+
11
+ Binding store API: `register_binding`, `load_binding`, `recover_binding`,
12
+ `list_bindings`, and `remove_binding`. `git worktree remove` and vanish stay
13
+ in the consumer.
14
+
15
+ Rust is the implementation SSOT. Python is a shim, typing stubs, contract tests,
16
+ and a thin audit CLI. Missing native support is an install error; there is no
17
+ silent Python fallback in this project.
18
+
19
+ ## Scope
20
+
21
+ This crate answers four questions and records consumer mutations:
22
+
23
+ | Responsibility | What the library does |
24
+ | --- | --- |
25
+ | Identity | Resolve project id, main branches, GitHub policy, and the `NOVELER_*` / location env catalog from cwd, env, git, and project config. |
26
+ | Binding | Persist, load, recover, and validate a session↔worktree record (`WorktreeBinding`). Reject before treating a cwd as bound. |
27
+ | Checkout | Resolve a `GitWorktree` (`rev-parse`), classify main vs linked, list porcelain entries, emit reason codes. |
28
+ | Path | Resolve worktree base / dir / state dir / known bases with fixed precedence. `NOVELER_WORKTREE_BASE` and `config/worktree.yaml` overrides apply only when `CI` / `GITHUB_ACTIONS` is set; elsewhere the base is `~/.adaptive-task/worktrees/<project_id>`. Normalize and fingerprint paths. |
29
+
30
+ Audit (`record_event` / `read_records` / rotation, plus
31
+ `presentation/audit_cli.py`) is append-only logging of consumer git
32
+ mutations (`worktree_remove`, `branch_delete`). It is not a session log.
33
+
34
+ ### Out of scope
35
+
36
+ - **Agent isolation.** Codex Desktop, Cursor, and Claude Code create a
37
+ worktree (or checkout) so one chat does not dirty the user's main tree.
38
+ That sandbox is a different layer. This package does not replace it.
39
+ - **Session orchestration.** `adaptive-task init` / `next` / `report` stay
40
+ in the consumer.
41
+ - **Prep / vanish / cleanup hooks.** Detecting a disappeared worktree,
42
+ refusing cleanup, and running merge-cleanup tooling stay in adaptive-task.
43
+ - **`git worktree add` / `remove` as a product operation.** This crate
44
+ resolves locations and validates state. The consumer mutates git.
45
+
46
+ Using Codex Desktop only as an editor does not require importing this
47
+ package. Running adaptive-task identity, binding, or path policy still does.
48
+ Agent isolation worktrees and session worktrees can nest. Config may move
49
+ the session base when `CODEX_CLI` or `CLAUDE_CODE` is set. That override
50
+ exists because the two layers meet; it does not make this library redundant.
51
+
52
+ ## Architecture
53
+
54
+ ```mermaid
55
+ flowchart LR
56
+ callers["consumers"] -->|"worktree_runtime.api"| shim["Python shim and stubs"]
57
+ shim --> pyo3["PyO3 binding"]
58
+ pyo3 --> core["Rust core"]
59
+ contract["contract pytest"] -->|"black-box"| shim
60
+ cargo["cargo test"] --> core
61
+ release["maturin wheels"] --> callers
62
+ ```
63
+
64
+ ## Layout
65
+
66
+ Layout rationale is in [DESIGN.md](DESIGN.md).
67
+
68
+ - `crates/worktree_runtime_core/` — behavior SSOT
69
+ - `crates/worktree_runtime_pyo3/` — Python C-API translation only
70
+ - `src/worktree_runtime/` — `api` re-export, `.pyi`, `SURFACE.yaml`, `presentation/audit_cli.py`
71
+ - `src/_worktree_runtime_rust/` — placeholder package that hosts the cdylib built by maturin (`python-source = "src"`)
72
+ - `tests/contract/` — `from worktree_runtime.api import ...` only; `tests/cli/` — audit CLI exit codes
73
+
74
+ Distribution name is `worktree-runtime`. Import name is `worktree_runtime`.
75
+ `pyproject.toml` version and `Cargo.toml` `workspace.package.version` must match.
76
+ The crate is proprietary (`LICENSE`; all rights reserved). CI uses the `stable` toolchain with rustfmt and clippy.
77
+
78
+ ## Change protocol
79
+
80
+ Multi-file, refactor, behavior, or core+pyo3+facade+contract work: run
81
+ `adaptive-gate` first ([AGENTS.md](AGENTS.md) Routing). Testing policy:
82
+ `.cursor/rules/testing.mdc`.
83
+
84
+ 1. Behavior: add a failing cargo execution test, then implement in
85
+ `worktree_runtime_core` and run `cargo test`.
86
+ 2. Public-surface shape only: update `SURFACE.yaml` and `.pyi`, plus a thin
87
+ contract assertion under `tests/contract/`. Do not put behavior suites in
88
+ pytest.
89
+ 3. Export through PyO3; keep the shim as a re-export.
90
+ 4. When the public surface changed, build a wheel and run the contract and
91
+ CLI pytest. `cargo fmt --check` and `clippy -D warnings` are required.
92
+
93
+ The verify loop (commands, hook setup, release steps) is defined once in
94
+ [BUILD.md](BUILD.md).
95
+
96
+ ## Build
97
+
98
+ See [BUILD.md](BUILD.md). CI always runs fmt, clippy, cargo test, maturin,
99
+ contract+CLI pytest, `mypy.stubtest`, and an MSRV check, through
100
+ `.github/actions/rust-checks`.
101
+ Wheels are `abi3-py310` (CPython 3.10+) for Windows amd64, macOS x86_64 and
102
+ arm64, and manylinux x86_64 / aarch64. Pure-Python wheels are not published.
@@ -0,0 +1,50 @@
1
+ # worktree_runtime_core
2
+
3
+ This crate is the behavior SSOT. Do not add PyO3 or Python dependencies.
4
+ Behavior changes belong here with `cargo test` (`.cursor/rules/testing.mdc`);
5
+ do not treat pytest as the SSOT.
6
+
7
+ ## Layers
8
+
9
+ - `domain/` — value objects and pure rules (`binding`, `audit`, `paths`,
10
+ `porcelain`, `git_worktree`, `github_policy`). Today `paths::normalize_path`
11
+ still canonicalizes through the filesystem and `WorktreeBinding::new` calls
12
+ it; treat that as a known exception, do not add more I/O to `domain/`.
13
+ - `application/` — services that compose domain + infrastructure
14
+ (`binding_lifecycle`, `validation`, `git_worktree_service`). They own the
15
+ reason codes callers branch on.
16
+ - `infrastructure/` — filesystem, env, git subprocess, config readers.
17
+
18
+ ## Conventions that are not obvious from the code
19
+
20
+ - **git**: every subprocess goes through `infrastructure::git_adapter`.
21
+ `git_output` returns `Result<String, GitInvokeError>`; `Exit` means the
22
+ repository state is unusable (not a repo, detached HEAD), every other
23
+ variant means git itself could not run and callers report
24
+ `WORKTREE_GIT_UNAVAILABLE`. Do not collapse errors to `""`. Tests
25
+ simulate a missing binary with `common::MissingGit` (thread-local
26
+ `override_git_program`), never by clearing `PATH`.
27
+ - **Reason codes** are a public contract (adaptive-task branches on them).
28
+ Adding one needs a CHANGELOG entry; renaming one is a breaking change.
29
+ - **Binding store** (`infrastructure::binding_store`) is locked
30
+ (`bindings.json.lock`) and fail-closed: a corrupt file is an error, never
31
+ an empty store.
32
+ - **Audit** (`infrastructure::audit_*`) is fail-open by design: a failed
33
+ write prints `[WORKTREE-AUDIT-FAILOPEN] ...` to stderr and returns
34
+ `Err(AuditWriteError)`; the PyO3 layer decides whether to raise
35
+ (`strict`). Rotation runs under the append lock; `read_records` reads
36
+ archives too. The only `unsafe` in the crate is `libc::gethostname` in
37
+ `audit_writer.rs` (unix).
38
+ - **Config precedence**: `NOVELER_WORKTREE_BASE` and
39
+ `config/worktree.yaml` overrides are honored only when `CI` or
40
+ `GITHUB_ACTIONS` is set (`worktree_base_resolver.rs`). Outside CI the
41
+ base is `~/.adaptive-task/worktrees/<project_id>`. Parse failures in
42
+ `pyproject.toml` / `config/worktree.yaml` log a warning and fall back to
43
+ defaults; they are not distinguishable from "file absent" to callers.
44
+ - **project_id**: `resolve_project_id` returns the declared value unchecked;
45
+ `resolve_project_id_checked` rejects invalid ids; every function that
46
+ turns an id into a directory name (`resolve_state_dir_name`) validates it
47
+ and falls back to `noveler`.
48
+ - **Logging**: `log::debug!` / `log::warn!` for diagnostics; the two
49
+ `eprintln!` breadcrumbs (`failopen_message`, `suspicious_extra_message`)
50
+ are frozen by tests and stay on stderr.