@autosk/worktree 0.1.1 → 0.1.3

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 (3) hide show
  1. package/README.md +21 -13
  2. package/package.json +2 -4
  3. package/src/index.ts +15 -29
package/README.md CHANGED
@@ -22,8 +22,11 @@ autosk.registerWorkflow({
22
22
  ```
23
23
 
24
24
  Each isolated task runs in its own checkout so concurrent tasks never collide on
25
- the working tree. The engine calls `acquire` before scheduling a session (its
26
- returned `cwd` becomes `ctx.cwd`) and `release` on every transition.
25
+ the working tree. The engine calls `acquire` before scheduling each session (its
26
+ returned `cwd` becomes `ctx.cwd`) and `reap` only on a terminal transition
27
+ (`done`/`cancel`). This provider has no live env to stop, so it **omits**
28
+ `release` entirely — keeping the checkout on disk across sibling/human-park steps
29
+ is exactly the absence of teardown.
27
30
 
28
31
  ## Behaviour
29
32
 
@@ -35,22 +38,27 @@ either stack resolves to the same place:
35
38
  branch = autosk/<task-id>
36
39
  ```
37
40
 
38
- - **acquire** — allocates the per-task worktree on branch `autosk/<task-id>`
39
- (off `HEAD`), or re-uses it when a prior step kept it. A **missing** dir is
40
- re-allocated on the *existing* branch (v1 "missing worktree auto-recovery").
41
- Returns `{ cwd, meta: { branch, projectRoot } }`.
42
- - **release(`{terminal:true}`)** (done / cancel) removes the worktree dir but
41
+ - **acquire** (ensure-ready) — allocates the per-task worktree on branch
42
+ `autosk/<task-id>` (off `HEAD`), or re-uses it when a prior step kept it. A
43
+ **missing** dir is re-allocated on the *existing* branch (v1 "missing worktree
44
+ auto-recovery"). Idempotent and re-entered per step. Returns
45
+ `{ cwd, meta: { branch, projectRoot } }`.
46
+ - **(no `release`)** — sibling step and human-park keep the dir on disk
47
+ untouched, so the next `acquire` re-uses the same checkout. There is nothing to
48
+ quiesce, so the method is omitted.
49
+ - **reap** (destroy-on-terminal, done / cancel) — keyed by `(projectRoot,
50
+ taskId)`, so it works with no live handle (engine terminal, a manual
51
+ `done`/`cancel` after a park, or crash recovery). Removes the worktree dir but
43
52
  **preserves** the `autosk/<task-id>` branch (so the work survives for review /
44
- merge).
45
- - **release(`{terminal:false}`)** (sibling step / human-park) — keeps the dir so
46
- the next step re-uses the same checkout.
53
+ merge). With `force:false` it refuses to discard uncommitted changes and
54
+ reports `{ removed:false, dirty:true }`; `force:true` removes regardless.
47
55
 
48
56
  ### Failure handling
49
57
 
50
58
  The provider **only throws descriptive messages** — the engine wraps them
51
- (`isolation_acquire_failed: …` on acquire, `isolation_release_failed: …` on a
52
- happy-path release) and parks the task to `human`. It never parks or formats
53
- those prefixes itself. Throw cases:
59
+ (`isolation_acquire_failed: …` on acquire, `isolation_reap_failed: …` on a
60
+ terminal reap) and parks the task to `human`. It never parks or formats those
61
+ prefixes itself. Throw cases:
54
62
 
55
63
  - **non-git root** — `not a git repository: <root>` (the project root isn't a
56
64
  git repo).
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@autosk/worktree",
3
- "version": "0.1.1",
3
+ "version": "0.1.3",
4
4
  "description": "Shipped autoskd v2 isolation provider: per-task git worktree isolation (worktreeIsolation()) attachable to any workflow.",
5
5
  "license": "MIT",
6
6
  "author": "wierdbytes",
@@ -15,9 +15,7 @@
15
15
  },
16
16
  "keywords": [
17
17
  "autosk",
18
- "autosk-extension",
19
- "git-worktree",
20
- "isolation"
18
+ "autosk-isolation"
21
19
  ],
22
20
  "type": "module",
23
21
  "publishConfig": {
package/src/index.ts CHANGED
@@ -2,12 +2,15 @@
2
2
  * `@autosk/worktree` — the shipped `worktreeIsolation()` provider (plan §3.5).
3
3
  *
4
4
  * Ports v1's per-task git worktree isolation onto the v2
5
- * {@link IsolationProvider} contract. The
6
- * engine (`daemon/core/src/engine/session.ts`) calls `acquire` before scheduling
7
- * an isolated session and `release` on every transition; FAILURES ARE WRAPPED BY
8
- * THE ENGINE (`acquire` throw `isolation_acquire_failed: <msg>`, happy-path
9
- * `release` throw `isolation_release_failed: <msg>`), so this provider just
10
- * throws descriptive messages it never parks or formats those prefixes itself.
5
+ * {@link IsolationProvider} contract. The engine
6
+ * (`daemon/core/src/engine/session.ts`) calls `acquire` before scheduling an
7
+ * isolated session (idempotently re-used across the run's steps) and `reap` on a
8
+ * terminal transition (`done`/`cancel`). A worktree has nothing to "stop", so
9
+ * this provider omits `release` entirely keeping the dir across a step→step or
10
+ * a human-park IS the absence of teardown. FAILURES ARE WRAPPED BY THE ENGINE
11
+ * (`acquire` throw → `isolation_acquire_failed: <msg>`, `reap` throw →
12
+ * `isolation_reap_failed: <msg>`), so this provider just throws descriptive
13
+ * messages — it never parks or formats those prefixes itself.
11
14
  *
12
15
  * Deterministic mapping (byte-identical to the v1 Go/Rust slug so a worktree
13
16
  * allocated by either stack resolves to the same place):
@@ -42,7 +45,7 @@ export const WORKTREE_TAG = "worktree";
42
45
  /** Provider-internal bookkeeping carried on every {@link IsolationHandle}. */
43
46
  interface WorktreeMeta extends Record<string, unknown> {
44
47
  branch: string;
45
- /** The canonical project root `release` needs it to drive `git -C <root>`. */
48
+ /** The canonical project root the worktree was checked out from. */
46
49
  projectRoot: string;
47
50
  }
48
51
 
@@ -50,9 +53,9 @@ interface WorktreeMeta extends Record<string, unknown> {
50
53
  * Builds the shipped worktree {@link IsolationProvider}. Attach it to a workflow
51
54
  * via `isolation: worktreeIsolation()` (plan §3.6). `acquire` allocates (or
52
55
  * re-uses / re-allocates) the per-task worktree and hands the engine its path as
53
- * `ctx.cwd`; `release({terminal:true})` removes the dir but PRESERVES the
54
- * `autosk/<task>` branch, `release({terminal:false})` keeps the dir so the next
55
- * sibling step re-uses it.
56
+ * `ctx.cwd`; `reap` removes the dir on a terminal transition but PRESERVES the
57
+ * `autosk/<task>` branch. There is no `release`: the dir is simply kept across
58
+ * step→step and human-park (the env is reused on the next `acquire`).
56
59
  */
57
60
  export function worktreeIsolation(opts: WorktreeIsolationOptions = {}): IsolationProvider {
58
61
  const gitBin = opts.gitBin ?? "git";
@@ -95,23 +98,6 @@ export function worktreeIsolation(opts: WorktreeIsolationOptions = {}): Isolatio
95
98
  return { cwd: path, meta };
96
99
  },
97
100
 
98
- async release(handle, { terminal, force }): Promise<void> {
99
- // Non-terminal (sibling step / human-park): keep the dir so the next step
100
- // re-uses the same checkout. Nothing to do.
101
- if (!terminal) return;
102
- const meta = (handle.meta ?? {}) as Partial<WorktreeMeta>;
103
- const path = handle.cwd;
104
- const canon = meta.projectRoot ? canonRoot(meta.projectRoot) : "";
105
- await ensureGitAvailable(gitBin);
106
- const r = await cleanupTerminal(gitBin, canon, path, force);
107
- // The engine drives a terminal `release` with `force:true`, so this never
108
- // fires there; an explicit `force:false` caller gets a descriptive throw it
109
- // can surface (the engine would wrap it as `isolation_release_failed:`).
110
- if (r.dirty && !r.removed) {
111
- throw new Error(`worktree_dirty: ${path}${r.detail ? ` (${r.detail})` : ""}`);
112
- }
113
- },
114
-
115
101
  async reap({ projectRoot, taskId }, { force }): Promise<IsolationReapResult> {
116
102
  const canon = canonRoot(projectRoot);
117
103
  const path = pathFor(canon, taskId, opts.home);
@@ -186,8 +172,8 @@ async function verifyHealthy(gitBin: string, canon: string, path: string): Promi
186
172
  }
187
173
 
188
174
  /**
189
- * The shared terminal-cleanup core for both {@link IsolationProvider.release}
190
- * (live handle) and {@link IsolationProvider.reap} (session-free identity).
175
+ * The terminal-cleanup core behind {@link IsolationProvider.reap} (session-free,
176
+ * keyed by identity).
191
177
  *
192
178
  * Removes the worktree dir while PRESERVING its branch, gated on `force`:
193
179
  * `force:false` leaves a dirty checkout in place and reports `{dirty:true}` so