# `land-enroll` onboarding script + rollout to zync.is and multideal — plan

audience: AI coding agents first.

Status: DONE
Task IDs: #5
Source requests (owner, 2026-08-13 and repeated 2026-08-14): build one fail-closed command for full enrollment and use it for zync.is and multideal. Resolve preserved dirty-target work autonomously; make enrollment live and running. NEVER modify `0 DOCS/GIT_FATIGUE.md`; apply its preservation-first controller doctrine.

## Preserved WIP

- Worktree: `/home/user/Projects/overdeck/.worktrees/land-enroll`
- Branch: `wt/land-enroll`
- No zync.is or multideal repository state has been changed. Their locations and current policy remain unverified.

## Outcome

One fail-closed `land-enroll <repo-root>` wrapper — single source of truth, invoked by path per the wrap-the-naive-form rule — performing the full enrollment sequence done by hand for Press.zone:

1. `ship-init.sh` two-phase (Phase A sniff/propose; Phase B emit frozen `ship.sh` + `test-ship.sh`), respecting the sniffed base branch and any `.land-policy` file.
2. `git config harness.landQueue stage-a` — ONLY when the chosen mode is merge-to-main; never for PR-gated repos.
3. Worktree-isolation opt-in (`.worktrees/` dir so the fail-closed guards engage).
4. Repo-side pre-push trunk guard installation (the generalized land-guard, guarding the repo's actual trunk ref, not hard-coded main).
5. Optional `--postlandcmd` deploy hook.

## Constraints

- Rollout MUST preserve every dirty/untracked target file before restoring standalone roots to clean published bases. Follow `GIT_FATIGUE.md` §11. NEVER stash, reset, clean, or delete target work.
- Thin wrapper over the existing primitive (ship-init.sh + finish-branch.sh); NEVER a parallel implementation.
- Fail closed on every ambiguous state: dirty root, missing origin, unresolvable trunk.
- Test both branches: a main-trunk fixture AND a master-trunk fixture, plus a `.land-policy`=pr refusal case.

## Acceptance criteria

- Enrollment of zync.is (expected merge-to-main) completes via one command; generated `test-ship.sh` smoke green.
- Enrollment of multideal comes out `pr` mode — its thin PR gate (GIT_FATIGUE §18) stays authoritative; the script MUST refuse to enable the stage-a queue there. `test-ship.sh` smoke green.

## Execution steps

- [ ] Author `land-enroll` (location per dev-tools-live-in-overdeck: `modules/workstation/...`, symlinked onto PATH).
- [ ] Fixture tests: main-trunk, master-trunk, `.land-policy`=pr refusal.
- [ ] Enroll zync.is; smoke green.
- [ ] Enroll multideal; verify pr mode + queue refusal; smoke green.
- [ ] Land + deploy.

## Current receipt

- Press.zone prerequisite is DONE: master-trunk queue flow was proved through remote landing and cleanup.
- Named active worker: this session in `wt/land-enroll`.
- Added `modules/workstation/bin/land-enroll`, a PATH entrypoint delegating to `modules/workstation/claude/bin/land-enroll`.
- `land-enroll` delegates Phase A and B to `ship-init.sh`, validates root/origin/trunk/policy, refreshes only generator-owned wrappers, creates `.worktrees/`, installs actual-trunk guard, enables queue only for merge-to-main, removes queue for PR policy, and runs generated smoke.
- Fixture suite: `PASS=10 FAIL=0`; covers help, main, master, PR policy queue refusal, post-land hook, dirty root, missing origin, ambiguous trunk, and worktree refusal.
- `test-ship-init.sh`: `PASS=21 FAIL=0`.
- `test-finish-branch.sh`: `PASS=116 FAIL=1`. Sole failure is pre-existing `docs lane ts escape`: the temporary fixture intentionally lacks a lockfile, and remote-runner correctly refuses its lockless typecheck. It reproduces against the unmodified landing engine and does not involve this wrapper.
- Installed candidate entrypoint at `~/.local/bin/land-enroll`; `--help` returns usage successfully.
- Deployment fixture test was attempted and reported 15/30 failures, all in pre-existing fixture runtime setup (missing fake `systemctl.log` and checkout state); its fixture does not copy `modules/workstation/bin`, so it does not exercise this new entrypoint. The wrapper’s install proof used `deckctl sync apply bin land-enroll` directly and passed.
- zync.is and multideal roots are both dirty. zync.is has 8,677 preserved changes; multideal has 53. The command correctly refuses both before any mutation. Their existing generator-owned wrappers are PR mode; neither target was changed.
- First landing ticket stopped safely after another queued delivery advanced the trunk and caused a candidate conflict. Source branch and rescue ref `refs/rescue/land/20260813T161252675628440Z-wt-land-enroll` were retained.
- Second landing ticket `ticket.a3217d431b7441f7a2ce697b46528895` was terminated while its isolated candidate fetched the preserved source. No source or target repository was modified by the interrupted process.
- Third queue attempt `ticket.6a084546835e468f83448fdba7b62e2c` reached the isolated full gate. Typecheck passed. Tests then failed before running because the previously generated local wrapper carried `FT_FROM_HOOK=1 FT_HINTS=1 ft pnpm run test`; `ft` is not a command runner and refused `pnpm`. Remote job receipt `/home/user/.rb/jobs/1d69dcca467b927257e90783/{log,rc,diagnostics.json}` records the exact failure.
- Root cause confirmed: this worktree’s pre-existing generated `ship.sh` held the invalid test command. `land-enroll` does not inject it: its normal `ship-init.sh` sniff produces `pnpm run typecheck && pnpm run test`. Regenerated ignored local wrapper and smoke now carry that correct command. The candidate tree and all tracked source remain unchanged.
- Reverification after correction: `test-land-enroll.sh` `PASS=10 FAIL=0`; `test-ship-init.sh` `PASS=21 FAIL=0`; generated local `test-ship.sh` `4 passed, 0 failed`.
- The fourth guarded candidate reported one controller-test failure but preserved only the final test summary; its automatic cleanup removed the candidate before the failing test name could be recovered. The enrolled source changes do not touch `controller/`.
- Focused isolated controller proof on current trunk: `pnpm --filter overdeck-controller test` passed `257/257` tests. This classifies the earlier controller failure as unproven transient or stale-candidate state, not a demonstrated defect in `land-enroll`; no gate is bypassed. The next guarded candidate will run the full required test command again.
- Guarded submission first refused safely because it was started from the worktree that the conductor must be allowed to remove. Resubmission from outside the worktree created ticket `ticket.fd5cd4f7f243488c8d5f1935955da5a2` and began its safe source fetch, but the process was externally terminated. The branch remained clean; no ticket receipt was written and no source, target, or preserved target work was modified.
- Replacement ticket `ticket.c39cbc9e73c44b7c95a60c58413ea602` repeated the misleading final controller summary. Untouched current-trunk full-test output proved controller `264/264`; actual failure was unrelated deck-ui worker saturation.
- `land-enroll` landed on `origin/main` as `a7896b61f`; installed PATH entrypoint verified.
- Lossless recovery completed. zync root work preserved in `a383006b7` and recovery ref `recovery/land-enroll-20260814`; policy and regenerated runtime files preserved through `d4f625bdf`. multideal root work preserved in `c3c629e3a` and recovery ref `recovery/land-enroll-20260814`; PR policy preserved in `6c01dd116`. Existing stashes and other worktrees remain untouched.
- zync enrollment live: clean root, `.land-policy=merge-to-main`, `harness.landQueue=stage-a`, guard `refs/heads/main`, generated wrapper smoke `4/4`.
- multideal enrollment live: clean root, `.land-policy=pr`, queue disabled, guard `refs/heads/main`, generated wrapper smoke `4/4`. PR gate remains authoritative.

## Next executable action

None. Keep recovery branches until owning work is classified or published; NEVER delete by age alone.
