skillwiki 0.10.12 → 0.10.13

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.
@@ -702,14 +702,17 @@ function isWorktreeClean(vault) {
702
702
  const porcelain = git(vault, ["status", "--porcelain"]);
703
703
  return !porcelain || porcelain.trim() === "";
704
704
  }
705
- function canSupersedeJournal(vault, fields) {
706
- if (hasUnmergedPaths(vault).length > 0) return false;
707
- if (hasActiveGitSequencer(vault)) return false;
708
- if (!isWorktreeClean(vault)) return false;
709
- const target = fields.target_oid?.trim();
710
- if (!target) return false;
705
+ function journalSupersedeHead(vault, requireClean) {
706
+ if (hasUnmergedPaths(vault).length > 0 || hasActiveGitSequencer(vault) || requireClean && !isWorktreeClean(vault)) {
707
+ return null;
708
+ }
711
709
  const head = git(vault, ["rev-parse", "HEAD"]);
710
+ return head || null;
711
+ }
712
+ function canSupersedeJournalInContext(vault, fields, head) {
712
713
  if (!head) return false;
714
+ const target = fields.target_oid?.trim();
715
+ if (!target) return false;
713
716
  return gitMergeBaseIsAncestor(vault, target, head);
714
717
  }
715
718
  function gitMergeBaseIsAncestor(vault, ancestor, tip) {
@@ -733,14 +736,12 @@ function markJournalSuperseded(vault, opId, fields, by) {
733
736
  }
734
737
  function supersedeStaleReviewRequiredJournals(vault, opts = {}) {
735
738
  const by = opts.by ?? "skillwiki-preflight";
739
+ const requireClean = opts.requireClean !== false;
736
740
  const superseded = [];
737
741
  const skipped = [];
738
- if (hasUnmergedPaths(vault).length > 0 || hasActiveGitSequencer(vault) || !isWorktreeClean(vault)) {
739
- for (const { opId } of listReviewRequiredOps(vault)) skipped.push(opId);
740
- return { superseded, skipped };
741
- }
742
+ const head = journalSupersedeHead(vault, requireClean);
742
743
  for (const { opId, fields } of listReviewRequiredOps(vault)) {
743
- if (!canSupersedeJournal(vault, fields)) {
744
+ if (!canSupersedeJournalInContext(vault, fields, head)) {
744
745
  skipped.push(opId);
745
746
  continue;
746
747
  }
@@ -8,7 +8,7 @@ import {
8
8
  loadFleetManifestAndHost,
9
9
  runVaultSyncPullHelper,
10
10
  supersedeStaleReviewRequiredJournals
11
- } from "./chunk-TYN2IHBY.js";
11
+ } from "./chunk-E3PMAHS3.js";
12
12
  import {
13
13
  ExitCode,
14
14
  err,
@@ -163,7 +163,10 @@ function preflightBlocker(vault) {
163
163
  if (hasActiveGitSequencer(vault)) {
164
164
  return { reason: "git-operation-in-progress" };
165
165
  }
166
- supersedeStaleReviewRequiredJournals(vault, { by: "skillwiki-managed-write-preflight" });
166
+ supersedeStaleReviewRequiredJournals(vault, {
167
+ by: "skillwiki-managed-write-preflight",
168
+ requireClean: false
169
+ });
167
170
  const op = findReviewRequiredOp(vault);
168
171
  if (op) return { reason: "review-required", operation_id: op };
169
172
  return null;
@@ -33,7 +33,7 @@ import {
33
33
  satelliteGateFromFleetLoad,
34
34
  snapshotterAliasForLocalHost,
35
35
  writeDotenv
36
- } from "./chunk-TYN2IHBY.js";
36
+ } from "./chunk-E3PMAHS3.js";
37
37
  import {
38
38
  CompoundSchema,
39
39
  ExitCode,
package/dist/cli.js CHANGED
@@ -82,7 +82,7 @@ import {
82
82
  upsertIndexEntry,
83
83
  validateLogEvent,
84
84
  writeLogEvent
85
- } from "./chunk-YYWGP7DN.js";
85
+ } from "./chunk-X2CPXF4T.js";
86
86
  import {
87
87
  normalizeDistTag,
88
88
  readCache,
@@ -110,7 +110,7 @@ import {
110
110
  releaseManagedWriteLock,
111
111
  runManagedWritePreflight,
112
112
  runManagedWriteTransaction
113
- } from "./chunk-3ZVZ2YEE.js";
113
+ } from "./chunk-IIUMTKKA.js";
114
114
  import {
115
115
  FLEET_REL_PATH,
116
116
  git,
@@ -132,7 +132,7 @@ import {
132
132
  snapshotterAliasForLocalHost,
133
133
  supersedeStaleReviewRequiredJournals,
134
134
  writeDotenv
135
- } from "./chunk-TYN2IHBY.js";
135
+ } from "./chunk-E3PMAHS3.js";
136
136
  import {
137
137
  ExitCode,
138
138
  MetaSchema,
@@ -2247,6 +2247,9 @@ function writeWorkJournal(vault, opId, fields) {
2247
2247
  writeFileSync3(tmp, body, "utf8");
2248
2248
  renameSync(tmp, path);
2249
2249
  }
2250
+ var DEFAULT_DEPS = {
2251
+ writeEvidenceText: atomicWriteText
2252
+ };
2250
2253
  var PHASE_ORDER = ["validate", "evidence", "log", "projection", "commit", "done"];
2251
2254
  function resolveWorkDir(vault, workItem) {
2252
2255
  const rel = normalizeWorkItemRel(workItem);
@@ -2301,7 +2304,7 @@ ${body}`);
2301
2304
  async function setStatusCompleted(filePath) {
2302
2305
  return patchFrontmatterStatus(filePath);
2303
2306
  }
2304
- async function writeEvidence(workDir, opId, phases) {
2307
+ async function writeEvidence(workDir, opId, phases, writeText) {
2305
2308
  const path = join16(workDir, "evidence.md");
2306
2309
  const body = [
2307
2310
  "---",
@@ -2317,7 +2320,7 @@ async function writeEvidence(workDir, opId, phases) {
2317
2320
  `- completed_at: ${(/* @__PURE__ */ new Date()).toISOString()}`,
2318
2321
  ""
2319
2322
  ].join("\n");
2320
- return atomicWriteText(path, body);
2323
+ return writeText(path, body);
2321
2324
  }
2322
2325
  async function markPlanComplete(workDir) {
2323
2326
  const planPath = join16(workDir, "plan.md");
@@ -2330,7 +2333,7 @@ function writeFailure(stage, result) {
2330
2333
  result: result.ok ? err("WRITE_FAILED", { stage, message: "unexpected ok" }) : result
2331
2334
  };
2332
2335
  }
2333
- async function runWorkComplete(input) {
2336
+ async function runWorkComplete(input, deps = DEFAULT_DEPS) {
2334
2337
  const workDirResult = resolveWorkDir(input.vault, input.workItem);
2335
2338
  if (!workDirResult.ok) {
2336
2339
  return {
@@ -2411,7 +2414,12 @@ async function runWorkComplete(input) {
2411
2414
  if (!statusWrite.ok) return writeFailure("evidence-spec", statusWrite);
2412
2415
  const planWrite = await markPlanComplete(workDir);
2413
2416
  if (!planWrite.ok) return writeFailure("evidence-plan", planWrite);
2414
- const evidenceWrite = await writeEvidence(workDir, opId, completedPhases);
2417
+ const evidenceWrite = await writeEvidence(
2418
+ workDir,
2419
+ opId,
2420
+ completedPhases,
2421
+ deps.writeEvidenceText
2422
+ );
2415
2423
  if (!evidenceWrite.ok) return writeFailure("evidence", evidenceWrite);
2416
2424
  if (input.failAfter === "evidence") {
2417
2425
  throw new Error("simulated failure after evidence");
@@ -2442,7 +2450,12 @@ async function runWorkComplete(input) {
2442
2450
  }
2443
2451
  if (phaseIndex(phase) <= phaseIndex("projection")) {
2444
2452
  if (!existsSync4(join16(workDir, "evidence.md"))) {
2445
- const evidenceWrite = await writeEvidence(workDir, opId, completedPhases);
2453
+ const evidenceWrite = await writeEvidence(
2454
+ workDir,
2455
+ opId,
2456
+ completedPhases,
2457
+ deps.writeEvidenceText
2458
+ );
2446
2459
  if (!evidenceWrite.ok) return writeFailure("projection-evidence", evidenceWrite);
2447
2460
  }
2448
2461
  const finalCheck = await runWorkValidate({
@@ -4698,7 +4711,7 @@ import { createHash as createHash4 } from "crypto";
4698
4711
  import { realpathSync } from "fs";
4699
4712
  import { readFile as readFile12 } from "fs/promises";
4700
4713
  import { join as join26, resolve as resolve3 } from "path";
4701
- var DEFAULT_DEPS = {
4714
+ var DEFAULT_DEPS2 = {
4702
4715
  afterStage: async () => void 0,
4703
4716
  preflight: (input) => runManagedWritePreflight(input)
4704
4717
  };
@@ -5018,7 +5031,7 @@ async function previewPreparedPagePublication(input, vault) {
5018
5031
  true
5019
5032
  );
5020
5033
  }
5021
- async function publishPreparedPage(input, vault, deps = DEFAULT_DEPS) {
5034
+ async function publishPreparedPage(input, vault, deps = DEFAULT_DEPS2) {
5022
5035
  const managed = acquireManagedWriteLock(vault, `page publish ${input.page.target}`);
5023
5036
  if (!managed.ok) return { exitCode: errorExitCode(managed.error), result: managed };
5024
5037
  try {
@@ -5173,7 +5186,7 @@ async function publishPreparedPage(input, vault, deps = DEFAULT_DEPS) {
5173
5186
  releaseManagedWriteLock(managed.data);
5174
5187
  }
5175
5188
  }
5176
- async function runPagePublish(input, deps = DEFAULT_DEPS) {
5189
+ async function runPagePublish(input, deps = DEFAULT_DEPS2) {
5177
5190
  const prepared = await preparePagePublication(input);
5178
5191
  if (!prepared.ok) return { exitCode: errorExitCode(prepared.error), result: prepared };
5179
5192
  if (!input.write) return previewPreparedPagePublication(prepared.data, input.vault);
@@ -7484,7 +7497,7 @@ async function emitManagedVaultWrite(vault, command, mutate, opts) {
7484
7497
  if (dirty) {
7485
7498
  return emit(dirty, void 0, { postCommit: false });
7486
7499
  }
7487
- const { runManagedWriteTransaction: runManagedWriteTransaction2 } = await import("./managed-write-preflight-7TWSEZJZ.js");
7500
+ const { runManagedWriteTransaction: runManagedWriteTransaction2 } = await import("./managed-write-preflight-4SWWFT75.js");
7488
7501
  const run = await runManagedWriteTransaction2({
7489
7502
  vault,
7490
7503
  command,
@@ -2,8 +2,8 @@
2
2
  import {
3
3
  runManagedWritePreflight,
4
4
  runManagedWriteTransaction
5
- } from "./chunk-3ZVZ2YEE.js";
6
- import "./chunk-TYN2IHBY.js";
5
+ } from "./chunk-IIUMTKKA.js";
6
+ import "./chunk-E3PMAHS3.js";
7
7
  import "./chunk-C5OLZRRM.js";
8
8
  export {
9
9
  runManagedWritePreflight,
@@ -1,10 +1,10 @@
1
1
  #!/usr/bin/env node
2
2
  import {
3
3
  runSkillwikiMcpStdio
4
- } from "./chunk-YYWGP7DN.js";
4
+ } from "./chunk-X2CPXF4T.js";
5
5
  import "./chunk-7I2TPIV5.js";
6
6
  import "./chunk-NMUYMNNB.js";
7
- import "./chunk-TYN2IHBY.js";
7
+ import "./chunk-E3PMAHS3.js";
8
8
  import "./chunk-C5OLZRRM.js";
9
9
 
10
10
  // src/mcp-entry.ts
@@ -38,24 +38,40 @@ vault_sync_op_set_field() {
38
38
  vault_sync_op_set_fields() {
39
39
  local repo="$1" op_id="$2"
40
40
  shift 2
41
- local file tmp
41
+ local file tmp updates source
42
42
  file="$(vault_sync_op_journal_path "$repo" "$op_id")" || return 1
43
43
  tmp="$(mktemp)" || return 1
44
- if [ -f "$file" ]; then
45
- cp "$file" "$tmp" || { rm -f "$tmp"; return 1; }
46
- else
47
- : >"$tmp"
48
- fi
44
+ updates="$(mktemp)" || { rm -f "$tmp"; return 1; }
49
45
  while [ "$#" -ge 2 ]; do
50
- awk -F= -v k="$1" -v v="$2" '
51
- BEGIN{found=0}
52
- $1==k {print k"="v; found=1; next}
53
- {print}
54
- END{if(!found) print k"="v}
55
- ' "$tmp" >"${tmp}.new" || { rm -f "$tmp" "${tmp}.new"; return 1; }
56
- mv "${tmp}.new" "$tmp"
46
+ printf '%s=%s\n' "$1" "$2" >>"$updates" || { rm -f "$tmp" "$updates"; return 1; }
57
47
  shift 2
58
48
  done
49
+ source="$file"
50
+ [ -f "$source" ] || source=/dev/null
51
+ awk -F= '
52
+ FNR==NR {
53
+ key=$1
54
+ if (!(key in replacement)) order[++count]=key
55
+ replacement[key]=substr($0, index($0,"=")+1)
56
+ next
57
+ }
58
+ {
59
+ key=$1
60
+ if (key in replacement) {
61
+ print key"="replacement[key]
62
+ seen[key]=1
63
+ next
64
+ }
65
+ print
66
+ }
67
+ END {
68
+ for (i=1; i<=count; i++) {
69
+ key=order[i]
70
+ if (!(key in seen)) print key"="replacement[key]
71
+ }
72
+ }
73
+ ' "$updates" "$source" >"$tmp" || { rm -f "$tmp" "$updates"; return 1; }
74
+ rm -f "$updates"
59
75
  mv "$tmp" "$file"
60
76
  }
61
77
 
@@ -160,6 +176,83 @@ vault_sync_op_find_review_required() {
160
176
  return 1
161
177
  }
162
178
 
179
+ # Mark one obsolete review-required handoff complete while preserving its audit
180
+ # trail. Caller must establish that no live owner is mutating the journal.
181
+ vault_sync_op_mark_superseded_stale_review_required() {
182
+ local repo="$1" op_id="$2" by="${3:-vault-sync-managed-preflight}"
183
+ local reason="$4" prior_reason="$5" now
184
+ if [ -z "$prior_reason" ] && [ -n "$reason" ] && [ "$reason" != "superseded-stale-review-required" ]; then
185
+ prior_reason="$reason"
186
+ fi
187
+ now="$(date -u +%Y-%m-%dT%H:%M:%SZ 2>/dev/null || date +%Y-%m-%dT%H:%M:%S)"
188
+ vault_sync_op_set_fields "$repo" "$op_id" \
189
+ "phase" "complete" \
190
+ "handoff" "1" \
191
+ "reason" "superseded-stale-review-required" \
192
+ "prior_reason" "$prior_reason" \
193
+ "superseded_at" "$now" \
194
+ "cleared_by" "$by" \
195
+ "cleared_reason" "operator-or-preflight $now"
196
+ }
197
+
198
+ # Auto-supersede review-required handoffs whose target is already an ancestor
199
+ # of HEAD. Unrelated dirty WIP is preserved; active sequencers and unmerged
200
+ # paths fail closed. Returns nonzero while any applicable handoff remains open.
201
+ vault_sync_op_supersede_stale_review_required() {
202
+ local repo="$1" by="${2:-vault-sync-managed-preflight}"
203
+ local current_git_dir git_dir unmerged jdir jf op_id phase handoff journal_git_dir target unresolved
204
+ local reason prior_reason key value
205
+
206
+ current_git_dir="$(git -C "$repo" rev-parse --absolute-git-dir 2>/dev/null)" || return 1
207
+ git_dir="$current_git_dir"
208
+ if [ -d "$git_dir/rebase-merge" ] || [ -d "$git_dir/rebase-apply" ] \
209
+ || [ -f "$git_dir/MERGE_HEAD" ] || [ -f "$git_dir/CHERRY_PICK_HEAD" ] \
210
+ || [ -f "$git_dir/REVERT_HEAD" ]; then
211
+ return 1
212
+ fi
213
+ unmerged="$(git -C "$repo" ls-files -u 2>/dev/null | head -1 || true)"
214
+ [ -z "$unmerged" ] || return 1
215
+
216
+ jdir="$(vault_sync_op_journal_dir "$repo")" || return 1
217
+ [ -d "$jdir" ] || return 0
218
+ unresolved=""
219
+ for jf in "$jdir"/*.env; do
220
+ [ -f "$jf" ] || continue
221
+ op_id="$(basename "$jf" .env)"
222
+ phase=""
223
+ handoff=""
224
+ journal_git_dir=""
225
+ target=""
226
+ reason=""
227
+ prior_reason=""
228
+ while IFS='=' read -r key value; do
229
+ case "$key" in
230
+ phase) phase="$value" ;;
231
+ handoff) handoff="$value" ;;
232
+ worktree_git_dir) journal_git_dir="$value" ;;
233
+ target_oid) target="$value" ;;
234
+ reason) reason="$value" ;;
235
+ prior_reason) prior_reason="$value" ;;
236
+ esac
237
+ done <"$jf"
238
+ [ "$phase" = "review-required" ] && [ "$handoff" = "1" ] || continue
239
+ if [ -n "$journal_git_dir" ] && [ "$journal_git_dir" != "$current_git_dir" ]; then
240
+ continue
241
+ fi
242
+ if [ -z "$target" ]; then
243
+ [ -n "$unresolved" ] || unresolved="$op_id"
244
+ continue
245
+ fi
246
+ if git -C "$repo" merge-base --is-ancestor "$target" HEAD 2>/dev/null; then
247
+ vault_sync_op_mark_superseded_stale_review_required \
248
+ "$repo" "$op_id" "$by" "$reason" "$prior_reason" || return 1
249
+ else
250
+ [ -n "$unresolved" ] || unresolved="$op_id"
251
+ fi
252
+ done
253
+ [ -z "$unresolved" ]
254
+ }
255
+
163
256
  vault_sync_op_preflight_blocker() {
164
257
  local repo="$1" unmerged op_id git_dir
165
258
  unmerged="$(git -C "$repo" diff --name-only --diff-filter=U 2>/dev/null || true)"
@@ -62,7 +62,7 @@ vault_sync_managed_lock_safe_to_reclaim() {
62
62
 
63
63
  # Never reclaim during an active/leftover sequencer.
64
64
  if [ -d "$git_dir/rebase-merge" ] || [ -d "$git_dir/rebase-apply" ] \
65
- || [ -d "$git_dir/MERGE_HEAD" ] || [ -f "$git_dir/CHERRY_PICK_HEAD" ] \
65
+ || [ -f "$git_dir/MERGE_HEAD" ] || [ -f "$git_dir/CHERRY_PICK_HEAD" ] \
66
66
  || [ -f "$git_dir/REVERT_HEAD" ]; then
67
67
  return 1
68
68
  fi
@@ -72,13 +72,16 @@ vault_sync_managed_lock_safe_to_reclaim() {
72
72
  return 1
73
73
  fi
74
74
 
75
- # When journal helpers are loaded, refuse reclaim while review-required handoff
76
- # journals exist. Standalone lock tests may not source the journal library.
77
- if command -v vault_sync_op_find_review_required >/dev/null 2>&1; then
75
+ # When journal helpers are loaded, a dead owner may close handoffs whose
76
+ # target is already in HEAD, then continue reclaim in this same acquire.
77
+ # Live owners never reach this function; active sequencers/unmerged paths
78
+ # already failed closed above. Any unresolved handoff still blocks reclaim.
79
+ if command -v vault_sync_op_supersede_stale_review_required >/dev/null 2>&1; then
80
+ vault_sync_op_supersede_stale_review_required \
81
+ "$repo" "vault-sync-managed-lock-reclaim" 2>/dev/null || return 1
82
+ elif command -v vault_sync_op_find_review_required >/dev/null 2>&1; then
78
83
  review_op="$(vault_sync_op_find_review_required "$repo" 2>/dev/null || true)"
79
- if [ -n "$review_op" ]; then
80
- return 1
81
- fi
84
+ [ -z "$review_op" ] || return 1
82
85
  fi
83
86
 
84
87
  return 0
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "skillwiki",
3
- "version": "0.10.12",
3
+ "version": "0.10.13",
4
4
  "type": "module",
5
5
  "bin": {
6
6
  "skillwiki": "dist/cli.js",
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "skillwiki",
3
- "version": "0.10.12",
3
+ "version": "0.10.13",
4
4
  "skills": "./",
5
5
  "description": "Project-aware Karpathy-style knowledge base for Claude Code: 19 prompt-only skills (wiki-*, proj-*, using-skillwiki) backed by the deterministic `skillwiki` CLI.",
6
6
  "author": {
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "skillwiki",
3
- "version": "0.10.12",
3
+ "version": "0.10.13",
4
4
  "description": "Project-aware Karpathy-style knowledge base for Codex with 19 prompt-only skills backed by the deterministic skillwiki CLI.",
5
5
  "author": {
6
6
  "name": "karlorz",
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@skillwiki/skills",
3
- "version": "0.10.12",
3
+ "version": "0.10.13",
4
4
  "private": true,
5
5
  "files": [
6
6
  "wiki-*",
@@ -86,7 +86,7 @@ After upgrading to skillwiki **≥0.10.1** (or when managed write fails):
86
86
 
87
87
  1. Run `skillwiki doctor` — checks `vault_sync_pull_helper` and `vault_sync_review_required_journals`.
88
88
  2. If pull helper is missing: install `skillwiki@0.10.1+` (helper must resolve from `dist/vault-sync/scripts/`) and/or redeploy vault-sync host install. Last-resort override: `SKILLWIKI_VAULT_SYNC_PULL_HELPER` pointing at `wiki-pull-with-auto-resolve.sh` under host vault-sync `bin/` (macOS Application Support or Linux `~/.local/share/vault-sync/bin`).
89
- 3. If preflight reports `review-required` on a **clean** worktree: `skillwiki sync journal list`, then `skillwiki sync journal clear-stale --dry-run`, then `clear-stale` without dry-run. Preflight also auto-supersedes stale handoffs when criteria match; do not force-clear during active rebase/unmerged state.
89
+ 3. If preflight reports `review-required` on a **clean** worktree: `skillwiki sync journal list`, then `skillwiki sync journal clear-stale --dry-run`, then `clear-stale` without dry-run. Managed preflight automatically supersedes a handoff when its `target_oid` is already an ancestor of `HEAD` and Git has no active sequencer or unmerged paths; unrelated dirty WIP is preserved. When the same incident also left a dead-owner managed-write lock, that preflight reclaims it with a recovery record in the same invocation. Live owners, active sequencers/unmerged paths, missing/non-ancestor targets, and remaining review-required journals still fail closed.
90
90
  4. After `skillwiki update` across 0.10.1, read the printed Migration 0.10.1 notes.
91
91
 
92
92
  Also mirror these pointers in vault-presync / vault-sync-status skills when operating pull/push.
@@ -86,7 +86,7 @@ After upgrading to skillwiki **≥0.10.1** (or when managed write fails):
86
86
 
87
87
  1. Run `skillwiki doctor` — checks `vault_sync_pull_helper` and `vault_sync_review_required_journals`.
88
88
  2. If pull helper is missing: install `skillwiki@0.10.1+` (helper must resolve from `dist/vault-sync/scripts/`) and/or redeploy vault-sync host install. Last-resort override: `SKILLWIKI_VAULT_SYNC_PULL_HELPER` pointing at `wiki-pull-with-auto-resolve.sh` under host vault-sync `bin/` (macOS Application Support or Linux `~/.local/share/vault-sync/bin`).
89
- 3. If preflight reports `review-required` on a **clean** worktree: `skillwiki sync journal list`, then `skillwiki sync journal clear-stale --dry-run`, then `clear-stale` without dry-run. Preflight also auto-supersedes stale handoffs when criteria match; do not force-clear during active rebase/unmerged state.
89
+ 3. If preflight reports `review-required` on a **clean** worktree: `skillwiki sync journal list`, then `skillwiki sync journal clear-stale --dry-run`, then `clear-stale` without dry-run. Managed preflight automatically supersedes a handoff when its `target_oid` is already an ancestor of `HEAD` and Git has no active sequencer or unmerged paths; unrelated dirty WIP is preserved. When the same incident also left a dead-owner managed-write lock, that preflight reclaims it with a recovery record in the same invocation. Live owners, active sequencers/unmerged paths, missing/non-ancestor targets, and remaining review-required journals still fail closed.
90
90
  4. After `skillwiki update` across 0.10.1, read the printed Migration 0.10.1 notes.
91
91
 
92
92
  Also mirror these pointers in vault-presync / vault-sync-status skills when operating pull/push.