Split jaahd worktree mechanics into package

Task 7601 · Done · Coder · created · updated

## Requirement (verbatim, epic 7555) > Break large modules into smaller and more structured ones. E.g. "Very large modules: routes/[tasks.py](http://tasks.py) has 12,867 lines and [models.py](http://models.py) has 8,770." Settled upstream (PRD = task 7553, Architecture = task 7554): split the 26 files over 3,000 lines frozen on 2026-10-18 — one domain per module, one PR per module (paired tests ride along), every caller rewritten with no shim, external contracts unchanged, no size gate. This child is ONE of 21 independent PRs; no child depends on another. ## This child: `apps/jaahd/worktree_mechanics.py` (3,878 lines) → package `apps/jaahd/worktree_mechanics/`, plus its paired test `apps/jaahd/tests/test_worktree_mechanics.py` (4,766 lines) (Architecture §5.7) Modules: - `core.py`: `PoolConfig`, `slot_path`, `_run_git`, `_git`, `wt_branch`, `_ERASED`, `note_erased`. - `env_file.py`: `env_text`, `_ENV_TEXT_CACHE` (home of the shared cache), hardening, `_set_env_vars`. - `ports.py`: port band, auto ports, `_port_bindable`, `_AUTO_PORTS_LOCK`, folder, stack identity, `_PORT_VAR_RESERVED_NAMES`, `has_stack`. - `managed_env.py`; `processes.py` (proc start, cwd scan); `reap.py`. - `reset.py`: `reset_to_base`, sync, prune, `_PRUNE_BUDGET`. - `lifecycle.py`: roster, clone, create, claim, retire. Patch seam (C2): `wm._port_bindable` is patched at 59 sites and used inside `ports.py` only — it stays in one module and tests patch `ports`. Note: child P5 (reconcile) will import `has_stack`/`wt_branch` from wherever they live on `develop`; keep both names defined exactly once here. Repoint (C5): `apps/jaahd/tests/test_fleet_wire_contract.py:798–799` (slice at `def reset_to_base(` → `reset.py`); `tools/tests/test_node_credential_parity.py:105,120` (→ `ports.py`); `apps/jaahd/tests/test_host_platform_guard.py:47` — the per-file budget `{KIND_PROC: 11}` is re-keyed and split across the new files so the total stays ≤ 11. Importers: ~12 jaahd modules and 9 lazy sites in `worktree_ops.py`. Living docs: 4 doc files point at `worktree_mechanics.py`. Test split: `test_worktree_mechanics.py` splits along its banners — ports, slot/reset, production-source guards, git declarations, cwd, reap, pool scan, env I/O — into `test_worktree_mechanics_<facet>.py`; shared helpers to a sibling `worktree_mechanics_helpers.py`, not `conftest.py`. Bundle: `tools/build_jaahd_bundle.py:50` ships `apps/jaahd` via `rglob` — verify per O7. ## Shared rules (Architecture §3, binding) - C1 Package shape: `worktree_mechanics/__init__.py` is a docstring module map only — no names, no `__getattr__`, no `*`. - C2 Patch seams: a moved name that tests patch is reached by other new modules as `<module>.<name>` via `from worktree_mechanics import <module>`; tests patch it on the defining module; drop `raising=False` once the new home is confirmed. - C3 Codemod import forms: parenthesised `from X import a, b`; `import worktree_mechanics as wm` + `wm.attr`; string targets; `sys.modules[...]`; function-local imports stay function-local. - AD6 Codemod: a transient rewrite script (stdlib `ast` or `sg`; no libcst) kept in `$JAAH_REPORT_DIR`, never committed; re-run after every `git rebase origin/develop`. Scope: `apps/ tools/ infra/ packages/`. - C5 Source-reading and path-keyed tests/registries are repointed, never deleted or skipped. Discovery: `git grep -nF 'worktree_mechanics.py' -- tools/ apps/ infra/ packages/ '*.toml' '*.json' '*.yml' '*.sh'` (covers the old test path too); re-key every hit. - C6 `logging.getLogger(__name__)`; update logger-name assertions. - C7 Every living-doc pointer (incl. `apps/jaahd/README.md`) to the moved path points at the package dir or owning submodule; a deleted path never appears in backticks. - AD7 Test siblings `test_<subject>_<facet>.py`. - Every new module opens with a one-line docstring naming the one domain it owns; no `utils`/`common`/`helpers` grab-bag. - AD9 Delivery: merge lane is ON — do NOT arm auto-merge. Rebase on fresh `develop` and re-run the codemod immediately before each `pull_request_enqueue`; enqueue after every push. Watch Jaah Deploy: on failure fix and enqueue again; cancelled/superseded → leave it. - Run `./dc check full` only on the committed, rebased tree, never in parallel with `./dc test` on the same slot. - Small internal cleanups allowed only where no caller outside the module can observe them. Credentials & reach: - AC5, AC6, AC7, O4, O-patch, O-catalog, O-mappers, O-cycle, O7 build | None beyond the worktree; the slot's local `./dc` stack (project p1-6) | verified on node h2: `./dc up` exit 0; `./dc run api alembic check` → "No new upgrade operations detected."; the in-process `app.openapi()` returned 726 paths and 1,037 schemas - AC9 / AD9: PR view, enqueue, Jaah Deploy watch | `jaah-forge`, pre-authenticated for the task, plus the `pull_request_enqueue` control tool; GitHub `jaah-ai/jaah` | verified: `jaah-forge api get repos/jaah-ai/jaah/actions/workflows` from node h2 listed the "Jaah Deploy" workflow - AC10 dev UI check, O7 node-healthy check | `jaah/debug/main/operator-login` (roster; covers app.jaah.ai and app.dev.jaah.ai); env `https://app.dev.jaah.ai` | verified: `credential_get` succeeded (value not recorded); HTTPS GET of app.dev.jaah.ai from node h2 returned 200. `dev.jaah.ai` (internal ALB) timed out from h2, so it is not used. `gmind/skynet/dev` is on the roster but its base_url is an old host, so it is not used. - AC10 "a fleet runner claims a ready Task"; O7 "dev node healthy" | Same operator login; the console Nodes and Tasks pages on app.dev.jaah.ai | verified: signed in to app.dev.jaah.ai as the operator login in a browser from h2; the Nodes page listed 3 nodes, all `online` (including ts2-node6), which are the runners a deploy updates Out of scope: Do NOT touch `packages/agent-sdk/examples/python/**` (N1). Do NOT split any other file — `reconcile.py` and the other frozen files each have their own child (N7), and files ≤3,000 lines today are follow-ups (N2). Do NOT add a line-count ceiling, lint rule or gate (N3). Do NOT keep `worktree_mechanics` names importable through a shim, facade, `__getattr__` redirect, star re-export or alias module (N4). Do NOT change the node↔CP wire format, port grid, env-var names, CLI/`jaah-call` verbs, or user-visible UI (N5). No new features, dependency bumps or behavioural fixes riding along (N6). Do NOT rewrite git history or touch `apps/api/migrations/versions/` (N8). Not applicable to this jaahd-only PR: AC4/O4 openapi diff and AC5 alembic check. Done means: - AC1 No §4 file remains over 3,000 lines, and no module created by the split is over 3,000 lines. — Run `git ls-files | grep -E '\.(py|ts|tsx|js|sh)$' | grep -v -E 'node_modules|alembic/versions|migrations/|packages/agent-sdk/examples/' | xargs wc -l | awk '$1>3000 && $2!="total"' | sort -rn` on `develop` after the PR; observe `worktree_mechanics.py` and `test_worktree_mechanics.py` absent and no new path listed. - AC2 Each new module owns one domain and declares it (R2). Fails if a module is a grab-bag (`utils2`, `misc`, `part2`, `helpers` mixing domains) or its docstring names two unrelated domains. — `for f in <new files>; do head -3 "$f"; done` → every file opens with a one-line ownership docstring/comment; reviewer (code-review lane) confirms each module's contents match its stated domain and its name. Record the module→domain table in the PR body. - AC3 Old path removed, no shim (R3). — Architecture's restated AST oracle (stdlib-`ast` script in `$JAAH_REPORT_DIR`) over `apps/ tools/ infra/ packages/` for the top-level name `worktree_mechanics` fails on: (a) `from worktree_mechanics import N` where N is not a submodule file; (b) `import worktree_mechanics [as m]` with any `m.<attr>` naming a non-submodule; (c) `from worktree_mechanics.<sub> import N` where N is not defined in that submodule; (d) a string target whose first component after the package is not a submodule; (e) the package `__init__` AST check `python -c "import ast,sys;t=ast.parse(open(sys.argv[1]).read());print([n for n in t.body if not isinstance(n,(ast.Expr,ast.Import,ast.ImportFrom)) or isinstance(n,ast.ImportFrom) and n.names[0].name=='*'])" <init>` printing anything but `[]`. Cite the zero-finding output in the PR body. - O-patch: for every `setattr`, string-patch or `raising=False` target touched (incl. all 59 `_port_bindable` sites), `python -c "import importlib; m=importlib.import_module('<mod>'); getattr(m,'<name>')"` (from `apps/jaahd`) succeeds; list them in the PR body. - AC6 Full gate green. — `./dc check full --gate-result --timeout 1500` on the committed, rebased tree → `GATE_RESULT status=pass` (includes `tools/check_context.py`). - AC7 Coverage not reduced (R6). — Before/after: `pytest --collect-only -q | tail -1` for the jaahd and tools suites → after ≥ before; cite both numbers in the PR body. - AC8 Docs pointers current (R7). — `git grep -nF 'worktree_mechanics.py' -- '*.md' '*.toml' '*.json' '*.yml'` → 0 hits (except git-history-only prose); `python3 tools/check_context.py` → exit 0. - AC9 Delivered one PR per module (R4). — `jaah-forge pr view <n>` → diff touches only this module (+ its paired test, callers, docs); merged into `develop` through the merge lane after `pull_request_enqueue` (not auto-merge), shown MERGED. - AC11 jaahd package still ships (R8). — O7: `python3 tools/build_jaahd_bundle.py` succeeds and the staged tree contains every `worktree_mechanics/*.py`; after deploy a dev node reports healthy in the console Nodes page at `https://app.dev.jaah.ai`. - AC10 Availability on dev — the product still works for the operator after each PR deploys (no flag involved; refactor ships ON by deploy). — Watch **Jaah Deploy** for the merge commit succeed on GitHub; then at `https://app.dev.jaah.ai`: Nodes page lists nodes online with their worktree slots; create a Task `[split-check P7]`, Start it and observe a fleet runner claim it in a worktree (proves slot create/reset/ports), then delete it. Observe each succeeds as it did before the PR. - AC12 Quality: the result is more navigable, not just shorter. Fails if a split produces circular-import workarounds (function-local imports added only to dodge cycles), duplicated code across new modules, or modules so thin (one trivial function) that a reader must open many files to follow one flow. — Code-review lane on the PR: new function-local imports in the new files each justified in a comment; reviewer verdict recorded in the PR body.

Outcome

Split worktree_mechanics.py into an 8-module package and its test into 9 facets + helpers, every caller rewritten with no shim. Quick gate and 1,773 changed-area tests green; the lane's full gate passed and PR #6137 merged (77ca361f4). Review lanes passed. Deploy run superseded; AC10 dev check not done.

Pull request

Artifacts

Activity

  • Draft
  • Ready
  • Runner started
  • Runner ended (done)