@ai-dossier/sched 0.48.0 → 0.48.1

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.
package/README.md CHANGED
@@ -1124,6 +1124,8 @@ import {
1124
1124
  TRANSITIONS, // the transition tables themselves (for previews)
1125
1125
  buildStatusReport, // machine-readable status incl. blocked/failed sets
1126
1126
  // (+ optional `anchorSweep` → `anchors`, #768; null without it)
1127
+ // (+ optional `worktreeReader` → `kept-worktree` warnings, #791)
1128
+ keptWorktreeCandidates, buildKeptWorktreeWarnings, defaultKeptWorktreeReader, // #791
1127
1129
  sweepAnchors, classifyAnchor, membersShippedVerdict, anchorLedgerBlockers,
1128
1130
  shippingEvidence, // #768 anchor-close predicate (pure; readers injected)
1129
1131
  closeAnchor, // #768 idempotent comment-then-close, `-R <repo>` explicit
@@ -1327,10 +1329,14 @@ after-the-fact recovery, not a missing-data bug.
1327
1329
  can be manual (`sched pause`) or automatic (dispatch-health, #505/#629 above); `sched
1328
1330
  resume` clears the flag and both dispatch-health streaks.
1329
1331
 
1330
- ## Status health warnings (#776)
1332
+ ## Status health warnings (#776, #791)
1331
1333
 
1332
- `sched status` (and `warnings[]` in `--json`, built by `buildStatusWarnings`) flags state
1333
- nobody came back to, each with its exact remedy:
1334
+ `sched status` (and `warnings[]` in `--json`) flags state nobody came back to, each with
1335
+ its exact remedy. Most kinds come from `buildStatusWarnings`, pure over state + engine
1336
+ lease + clock; `kept-worktree` (below) comes from the separate `buildKeptWorktreeWarnings`,
1337
+ since it needs local git/fs reads — it only appears when the caller passes a
1338
+ `KeptWorktreeReader` to `buildStatusReport` (the CLI always does; a test harness omits it
1339
+ to skip the check entirely):
1334
1340
 
1335
1341
  - **`long-pause`** — paused for more than 24 h (`STATUS_HEALTH_WARNING_AGE_MS`), measured
1336
1342
  from `paused_at`, which `setPaused` stamps on the running → paused edge and clears on
@@ -1344,6 +1350,25 @@ nobody came back to, each with its exact remedy:
1344
1350
  closed issue sets the entry's sticky `stale_closed_at`, journals `stale-closed` once, and
1345
1351
  the recovery rail never respawns it (even after `sched resume`). Report slots are exempt:
1346
1352
  their issue is closed at merge by design. Remedy: `sched stop --issue N`.
1353
+ - **`kept-worktree`** (#791) — a `done` batch whose `worktree` or `member_worktree` is
1354
+ still set in the ledger: the `members-closed` stale-blocked reconcile below
1355
+ intentionally leaves it in place (it may hold unpushed operator repair work), and
1356
+ nothing else surfaced it until now. For each kept path, `sched status` reports:
1357
+ whether it still exists on disk; whether it is pool-claimed (the claim is held
1358
+ indefinitely until returned); and, via two local, read-only `git` probes scoped to
1359
+ that worktree's own `HEAD` (`git --no-optional-locks status --porcelain`, `git log
1360
+ HEAD --not --remotes`), whether it has uncommitted or unpushed work. The remedy is the
1361
+ exact `git worktree remove <path>` or `worktree-pool return --path <path>` command,
1362
+ properly shell-quoted — report-only, never executed. `reconcileKeptWorktrees`
1363
+ (`batch-dispatch.ts`, run every tick) separately clears the ledger's `worktree`/
1364
+ `pool_claimed` fields once there is DEFINITIVE evidence the cleanup already
1365
+ happened — the path no longer exists, or the pool itself reports it back as a warm
1366
+ spare — so the warning does not persist after the operator (or the pool) has already
1367
+ dealt with it; it never removes anything itself. A candidate whose path is currently
1368
+ claimed by a different, still in-flight batch is skipped (a returned pool worktree can
1369
+ be re-issued before the done batch's own field clears). Local-git-only, capped at 10
1370
+ probed candidates per run — no network call, so unlike `sched status --anchors` this
1371
+ runs by default.
1347
1372
 
1348
1373
  ## Batch anchors close only on positive evidence (#768)
1349
1374
 
@@ -1380,7 +1405,9 @@ predicate in `anchor-close.ts`) over `blocked`/`done` batches touched within the
1380
1405
  for #686's stale-blocked reconcile (`members-closed`), which survives the mandated
1381
1406
  rebase-merge that makes the ancestry probe structurally dead. It finishes the terminal
1382
1407
  rail inline but never tears the blocked worktree down (it may hold unpushed work);
1383
- the journal line says the worktree was kept.
1408
+ the journal line says the worktree was kept, and `sched status` then raises a
1409
+ `kept-worktree` warning for it on every run until the ledger field clears — see
1410
+ above and `reconcileKeptWorktrees` (#791).
1384
1411
 
1385
1412
  Everything else is surfaced, never closed: `sched status --anchors` (opt-in; `status`
1386
1413
  makes no GitHub call without it) lists each still-open anchor of a batch no longer in
@@ -1 +1 @@
1
- {"version":3,"file":"batch-dispatch.d.ts","sourceRoot":"","sources":["../src/batch-dispatch.ts"],"names":[],"mappings":"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"}
1
+ {"version":3,"file":"batch-dispatch.d.ts","sourceRoot":"","sources":["../src/batch-dispatch.ts"],"names":[],"mappings":"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"}
@@ -3024,6 +3024,72 @@ function reconcileStaleBlockedBatches(deps, now, result, anchorCtx) {
3024
3024
  }
3025
3025
  }
3026
3026
  }
3027
+ /**
3028
+ * #791: once a `done` batch's kept worktree (left in place by the
3029
+ * `members-closed` branch above) is DEFINITIVELY gone — the path no longer
3030
+ * exists on disk, or the pool itself now reports it back as a warm spare —
3031
+ * clear the ledger's `worktree`/`pool_claimed` (and, separately,
3032
+ * `member_worktree`/`member_pool_claimed`) fields so `sched status`'s
3033
+ * `kept-worktree` warning does not persist after the operator's cleanup
3034
+ * already happened. Ledger-only, by construction: this function reads
3035
+ * `fsExists` and `worktree-pool status --json` (both read-only) and never
3036
+ * builds a `git worktree remove` / `worktree-pool return` argv — the actual
3037
+ * removal stays entirely the operator's call, exactly like the warning
3038
+ * itself. Bounded the same way the warning's own candidate scan is: only
3039
+ * `done` batches carrying a kept worktree are considered, which in practice
3040
+ * is a small set.
3041
+ */
3042
+ function reconcileKeptWorktrees(deps, now) {
3043
+ const state = deps.store.load();
3044
+ const fsExists = deps.fsExists ?? ((p) => fs.existsSync(p));
3045
+ const candidates = state.batches.filter((b) => b.status === 'done' && (b.worktree !== null || b.member_worktree !== null));
3046
+ if (candidates.length === 0)
3047
+ return;
3048
+ const needsPoolCheck = candidates.some((b) => (b.worktree !== null && b.pool_claimed) ||
3049
+ (b.member_worktree !== null && b.member_pool_claimed));
3050
+ // One pool query per TICK, not per candidate — `worktree-pool status` is a
3051
+ // real subprocess (`npx ...`), and this reconcile is meant to be cheap.
3052
+ const poolStatusJson = needsPoolCheck
3053
+ ? deps.exec(teardown_1.POOL_BIN, [...teardown_1.POOL_ARGS_PREFIX, 'status', '--json'], deps.repoDir)
3054
+ : null;
3055
+ const goneOrReturned = (worktree, poolClaimed) => {
3056
+ if (!fsExists(worktree))
3057
+ return true;
3058
+ if (poolClaimed && poolStatusJson !== null) {
3059
+ return (0, teardown_1.poolEntryFor)(poolStatusJson, worktree) === 'warm';
3060
+ }
3061
+ return false;
3062
+ };
3063
+ for (const batch of candidates) {
3064
+ const clears = {};
3065
+ const clearedFields = [];
3066
+ if (batch.worktree !== null && goneOrReturned(batch.worktree, batch.pool_claimed)) {
3067
+ clears.worktree = null;
3068
+ clears.pool_claimed = false;
3069
+ clearedFields.push('worktree', 'pool_claimed');
3070
+ }
3071
+ if (batch.member_worktree !== null &&
3072
+ goneOrReturned(batch.member_worktree, batch.member_pool_claimed)) {
3073
+ clears.member_worktree = null;
3074
+ clears.member_pool_claimed = false;
3075
+ clearedFields.push('member_worktree', 'member_pool_claimed');
3076
+ }
3077
+ if (clearedFields.length === 0)
3078
+ continue;
3079
+ deps.store.withLock((s) => {
3080
+ const b = (0, state_1.findBatch)(s, batch.id);
3081
+ // Re-check under the lock: another tick, or an operator command, may
3082
+ // have already moved this batch or cleared the fields itself.
3083
+ if (!b || b.status !== 'done')
3084
+ return { state: s, result: undefined };
3085
+ return { state: (0, state_1.patchBatch)(s, batch.id, clears, now, false), result: undefined };
3086
+ });
3087
+ journalEvent(deps, 'kept-worktree-cleared', unit(batch.id), {
3088
+ cleared: clearedFields.join(','),
3089
+ detail: `kept-worktree ledger field(s) cleared on definitive evidence (${clearedFields.join(', ')}) — path gone or pool reports it returned; no removal command was run`,
3090
+ });
3091
+ }
3092
+ }
3027
3093
  function anchorTickContext(deps) {
3028
3094
  const reader = (0, groundtruth_1.issueCloseReader)(deps.groundTruth);
3029
3095
  if (reader === undefined || deps.anchorRepo === undefined)
@@ -4140,6 +4206,7 @@ function runBatchTick(deps, config, dispatch) {
4140
4206
  const anchorCtx = anchorTickContext(deps);
4141
4207
  reconcileStaleBlockedBatches(deps, now, result, anchorCtx);
4142
4208
  reconcileAnchorClosure(deps, now, anchorCtx);
4209
+ reconcileKeptWorktrees(deps, now);
4143
4210
  return result;
4144
4211
  }
4145
4212
  //# sourceMappingURL=batch-dispatch.js.map