skillwiki 0.9.56 → 0.9.58
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/dist/auto-update-bg.js +0 -1
- package/dist/{chunk-SE5URAJR.js → chunk-NIPZVIHT.js} +155 -0
- package/dist/cli.js +58 -11
- package/dist/skillwiki-mcp.js +1 -1
- package/package.json +1 -1
- package/skills/.claude-plugin/plugin.json +1 -1
- package/skills/.codex-plugin/plugin.json +1 -1
- package/skills/README.md +16 -2
- package/skills/package.json +1 -1
- package/skills/skills/wiki-sync/SKILL.md +20 -3
- package/skills/wiki-sync/SKILL.md +20 -3
package/dist/auto-update-bg.js
CHANGED
|
@@ -2888,6 +2888,7 @@ function buildCliSurface() {
|
|
|
2888
2888
|
syncCmd.command("lock").option("--summary <text>").option("--ttl-minutes <n>").option("--force").option("--wiki <name>");
|
|
2889
2889
|
syncCmd.command("unlock").option("--force").option("--wiki <name>");
|
|
2890
2890
|
syncCmd.command("peers").option("--wiki <name>");
|
|
2891
|
+
syncCmd.command("lint-delta").option("--base-ref <ref>").option("--wiki <name>");
|
|
2891
2892
|
const backupCmd = program.commands.find((c) => c.name() === "backup");
|
|
2892
2893
|
backupCmd.command("sync").option("--dry-run").option("--bucket <name>").option("--endpoint <url>").option("--region <region>").option("--prune").option("--wiki <name>");
|
|
2893
2894
|
backupCmd.command("restore").option("--bucket <name>").option("--endpoint <url>").option("--region <region>").option("--target <dir>").option("--wiki <name>");
|
|
@@ -4250,6 +4251,159 @@ ${split.data.body}`;
|
|
|
4250
4251
|
result: ok(input.summary ? summarizeLintOutput(output, input.examplesLimit) : output)
|
|
4251
4252
|
};
|
|
4252
4253
|
}
|
|
4254
|
+
function lintIssueFingerprint(bucket, item) {
|
|
4255
|
+
const page = extractIssuePage(item);
|
|
4256
|
+
const detail = normalizeIssueDetail(item);
|
|
4257
|
+
return `${bucket}\0${page}\0${detail}`;
|
|
4258
|
+
}
|
|
4259
|
+
function extractIssuePage(item) {
|
|
4260
|
+
if (typeof item === "string") {
|
|
4261
|
+
const m = item.match(/^([^:]+?)(?::\s|$)/);
|
|
4262
|
+
return (m?.[1] ?? item).trim();
|
|
4263
|
+
}
|
|
4264
|
+
if (item && typeof item === "object") {
|
|
4265
|
+
const obj = item;
|
|
4266
|
+
for (const key of ["path", "file", "page", "relPath"]) {
|
|
4267
|
+
if (typeof obj[key] === "string") return obj[key];
|
|
4268
|
+
}
|
|
4269
|
+
}
|
|
4270
|
+
return "";
|
|
4271
|
+
}
|
|
4272
|
+
function normalizeIssueDetail(item) {
|
|
4273
|
+
if (typeof item === "string") {
|
|
4274
|
+
return item.replace(/\s+/g, " ").trim();
|
|
4275
|
+
}
|
|
4276
|
+
try {
|
|
4277
|
+
return JSON.stringify(item, Object.keys(item).sort());
|
|
4278
|
+
} catch {
|
|
4279
|
+
return String(item);
|
|
4280
|
+
}
|
|
4281
|
+
}
|
|
4282
|
+
function collectLintErrorFingerprints(output) {
|
|
4283
|
+
const fps = /* @__PURE__ */ new Set();
|
|
4284
|
+
for (const bucket of output.by_severity.error) {
|
|
4285
|
+
for (const item of bucket.items) {
|
|
4286
|
+
fps.add(lintIssueFingerprint(bucket.kind, item));
|
|
4287
|
+
}
|
|
4288
|
+
}
|
|
4289
|
+
return fps;
|
|
4290
|
+
}
|
|
4291
|
+
async function runSyncLintDelta(input) {
|
|
4292
|
+
const { mkdtempSync, rmSync, existsSync: fsExists } = await import("fs");
|
|
4293
|
+
const { join: pathJoin } = await import("path");
|
|
4294
|
+
const { tmpdir } = await import("os");
|
|
4295
|
+
const { execFileSync: execFileSync2 } = await import("child_process");
|
|
4296
|
+
const vault = input.vault;
|
|
4297
|
+
const baseRef = input.baseRef ?? "origin/main";
|
|
4298
|
+
const days = input.days ?? 90;
|
|
4299
|
+
const lines = input.lines ?? 200;
|
|
4300
|
+
const logThreshold = input.logThreshold ?? 500;
|
|
4301
|
+
if (!fsExists(pathJoin(vault, ".git"))) {
|
|
4302
|
+
return {
|
|
4303
|
+
exitCode: ExitCode.VAULT_PATH_INVALID,
|
|
4304
|
+
result: err("NOT_A_GIT_REPO", { path: vault })
|
|
4305
|
+
};
|
|
4306
|
+
}
|
|
4307
|
+
try {
|
|
4308
|
+
execFileSync2("git", ["rev-parse", "--verify", baseRef], {
|
|
4309
|
+
cwd: vault,
|
|
4310
|
+
stdio: ["pipe", "pipe", "pipe"]
|
|
4311
|
+
});
|
|
4312
|
+
} catch {
|
|
4313
|
+
return {
|
|
4314
|
+
exitCode: ExitCode.LINT_HAS_ERRORS,
|
|
4315
|
+
result: err("LINT_DELTA_BASE_UNAVAILABLE", {
|
|
4316
|
+
baseRef,
|
|
4317
|
+
message: `base ref ${baseRef} does not resolve \u2014 fail closed`
|
|
4318
|
+
})
|
|
4319
|
+
};
|
|
4320
|
+
}
|
|
4321
|
+
const fullLint = await runLint({ vault, days, lines, logThreshold });
|
|
4322
|
+
if (!fullLint.result.ok) {
|
|
4323
|
+
return {
|
|
4324
|
+
exitCode: ExitCode.LINT_HAS_ERRORS,
|
|
4325
|
+
result: err("LINT_DELTA_FULL_FAILED", { detail: fullLint.result })
|
|
4326
|
+
};
|
|
4327
|
+
}
|
|
4328
|
+
const fullOutput = fullLint.result.data;
|
|
4329
|
+
if (!("by_severity" in fullOutput) || !fullOutput.by_severity) {
|
|
4330
|
+
return {
|
|
4331
|
+
exitCode: ExitCode.LINT_HAS_ERRORS,
|
|
4332
|
+
result: err("LINT_DELTA_MALFORMED", { message: "full lint missing by_severity" })
|
|
4333
|
+
};
|
|
4334
|
+
}
|
|
4335
|
+
const fullFps = collectLintErrorFingerprints(fullOutput);
|
|
4336
|
+
const tmpRoot = mkdtempSync(pathJoin(tmpdir(), "skillwiki-lint-delta-"));
|
|
4337
|
+
try {
|
|
4338
|
+
const archive = execFileSync2("git", ["archive", "--format=tar", baseRef], {
|
|
4339
|
+
cwd: vault,
|
|
4340
|
+
stdio: ["pipe", "pipe", "pipe"],
|
|
4341
|
+
maxBuffer: 256 * 1024 * 1024
|
|
4342
|
+
});
|
|
4343
|
+
execFileSync2("tar", ["-xf", "-"], {
|
|
4344
|
+
cwd: tmpRoot,
|
|
4345
|
+
input: archive,
|
|
4346
|
+
stdio: ["pipe", "pipe", "pipe"]
|
|
4347
|
+
});
|
|
4348
|
+
if (!fsExists(pathJoin(tmpRoot, "SCHEMA.md"))) {
|
|
4349
|
+
}
|
|
4350
|
+
const baseLint = await runLint({ vault: tmpRoot, days, lines, logThreshold });
|
|
4351
|
+
if (!baseLint.result.ok) {
|
|
4352
|
+
return {
|
|
4353
|
+
exitCode: ExitCode.LINT_HAS_ERRORS,
|
|
4354
|
+
result: err("LINT_DELTA_BASE_LINT_FAILED", {
|
|
4355
|
+
baseRef,
|
|
4356
|
+
detail: baseLint.result
|
|
4357
|
+
})
|
|
4358
|
+
};
|
|
4359
|
+
}
|
|
4360
|
+
const baseOutput = baseLint.result.data;
|
|
4361
|
+
if (!("by_severity" in baseOutput) || !baseOutput.by_severity) {
|
|
4362
|
+
return {
|
|
4363
|
+
exitCode: ExitCode.LINT_HAS_ERRORS,
|
|
4364
|
+
result: err("LINT_DELTA_MALFORMED", { message: "base lint missing by_severity" })
|
|
4365
|
+
};
|
|
4366
|
+
}
|
|
4367
|
+
const baseFps = collectLintErrorFingerprints(baseOutput);
|
|
4368
|
+
const newFps = [];
|
|
4369
|
+
const resolvedFps = [];
|
|
4370
|
+
for (const fp of fullFps) {
|
|
4371
|
+
if (!baseFps.has(fp)) newFps.push(fp);
|
|
4372
|
+
}
|
|
4373
|
+
for (const fp of baseFps) {
|
|
4374
|
+
if (!fullFps.has(fp)) resolvedFps.push(fp);
|
|
4375
|
+
}
|
|
4376
|
+
newFps.sort();
|
|
4377
|
+
resolvedFps.sort();
|
|
4378
|
+
const fullList = [...fullFps].sort();
|
|
4379
|
+
const output = {
|
|
4380
|
+
full_errors: fullFps.size,
|
|
4381
|
+
base_errors: baseFps.size,
|
|
4382
|
+
new_errors: newFps.length,
|
|
4383
|
+
resolved_errors: resolvedFps.length,
|
|
4384
|
+
full_fingerprints: fullList,
|
|
4385
|
+
new_fingerprints: newFps,
|
|
4386
|
+
resolved_fingerprints: resolvedFps,
|
|
4387
|
+
base_ref: baseRef,
|
|
4388
|
+
humanHint: newFps.length > 0 ? `lint delta: ${newFps.length} new error(s) vs ${baseRef} (full=${fullFps.size}, base=${baseFps.size}, resolved=${resolvedFps.length})` : fullFps.size > 0 ? `lint delta: 0 new errors vs ${baseRef}; inherited full_errors=${fullFps.size} (base=${baseFps.size}, resolved=${resolvedFps.length})` : `lint delta: clean (0 errors) vs ${baseRef}`
|
|
4389
|
+
};
|
|
4390
|
+
const exitCode = output.new_errors > 0 ? ExitCode.LINT_HAS_ERRORS : fullLint.exitCode === ExitCode.LINT_HAS_WARNINGS ? ExitCode.LINT_HAS_WARNINGS : ExitCode.OK;
|
|
4391
|
+
return { exitCode, result: ok(output) };
|
|
4392
|
+
} catch (e) {
|
|
4393
|
+
return {
|
|
4394
|
+
exitCode: ExitCode.LINT_HAS_ERRORS,
|
|
4395
|
+
result: err("LINT_DELTA_ARCHIVE_FAILED", {
|
|
4396
|
+
baseRef,
|
|
4397
|
+
message: String(e)
|
|
4398
|
+
})
|
|
4399
|
+
};
|
|
4400
|
+
} finally {
|
|
4401
|
+
try {
|
|
4402
|
+
rmSync(tmpRoot, { recursive: true, force: true });
|
|
4403
|
+
} catch {
|
|
4404
|
+
}
|
|
4405
|
+
}
|
|
4406
|
+
}
|
|
4253
4407
|
|
|
4254
4408
|
// src/commands/config.ts
|
|
4255
4409
|
import { readFile as readFile13 } from "fs/promises";
|
|
@@ -9507,6 +9661,7 @@ export {
|
|
|
9507
9661
|
fixPathTooLong,
|
|
9508
9662
|
assessSourceIdentity,
|
|
9509
9663
|
runLint,
|
|
9664
|
+
runSyncLintDelta,
|
|
9510
9665
|
configPath,
|
|
9511
9666
|
runConfigGet,
|
|
9512
9667
|
runConfigSet,
|
package/dist/cli.js
CHANGED
|
@@ -66,6 +66,7 @@ import {
|
|
|
66
66
|
runQuery,
|
|
67
67
|
runSkillwikiMcpStdio,
|
|
68
68
|
runStale,
|
|
69
|
+
runSyncLintDelta,
|
|
69
70
|
runTagAudit,
|
|
70
71
|
runTopicMapCheck,
|
|
71
72
|
runValidate,
|
|
@@ -76,7 +77,7 @@ import {
|
|
|
76
77
|
snapshotterAliasForLocalHost,
|
|
77
78
|
splitFrontmatter,
|
|
78
79
|
writeDotenv
|
|
79
|
-
} from "./chunk-
|
|
80
|
+
} from "./chunk-NIPZVIHT.js";
|
|
80
81
|
import {
|
|
81
82
|
normalizeDistTag,
|
|
82
83
|
readCache,
|
|
@@ -3725,15 +3726,51 @@ async function runSyncPush(input) {
|
|
|
3725
3726
|
})
|
|
3726
3727
|
};
|
|
3727
3728
|
}
|
|
3728
|
-
|
|
3729
|
-
|
|
3730
|
-
|
|
3731
|
-
|
|
3732
|
-
|
|
3733
|
-
|
|
3734
|
-
|
|
3735
|
-
|
|
3736
|
-
|
|
3729
|
+
let delta = { full_errors: 0, base_errors: 0, new_errors: 0, resolved_errors: 0 };
|
|
3730
|
+
const preferredBase = git(vault, ["rev-parse", "--verify", "origin/main"]) ? "origin/main" : git(vault, ["rev-parse", "--verify", "origin/HEAD"]) ? "origin/HEAD" : "";
|
|
3731
|
+
if (preferredBase) {
|
|
3732
|
+
const deltaResult = await runSyncLintDelta({ vault, baseRef: preferredBase });
|
|
3733
|
+
if (!deltaResult.result.ok) {
|
|
3734
|
+
return {
|
|
3735
|
+
exitCode: ExitCode.LINT_HAS_ERRORS,
|
|
3736
|
+
result: err("LINT_DELTA_UNAVAILABLE", {
|
|
3737
|
+
message: "lint-delta evidence missing or failed \u2014 fail closed",
|
|
3738
|
+
detail: deltaResult.result
|
|
3739
|
+
})
|
|
3740
|
+
};
|
|
3741
|
+
}
|
|
3742
|
+
delta = deltaResult.result.data;
|
|
3743
|
+
if (delta.new_errors > 0) {
|
|
3744
|
+
return {
|
|
3745
|
+
exitCode: ExitCode.LINT_HAS_ERRORS,
|
|
3746
|
+
result: err("LINT_NEW_ERRORS_BLOCK_PUSH", {
|
|
3747
|
+
full_errors: delta.full_errors,
|
|
3748
|
+
base_errors: delta.base_errors,
|
|
3749
|
+
new_errors: delta.new_errors,
|
|
3750
|
+
resolved_errors: delta.resolved_errors,
|
|
3751
|
+
new_fingerprints: deltaResult.result.data.new_fingerprints
|
|
3752
|
+
})
|
|
3753
|
+
};
|
|
3754
|
+
}
|
|
3755
|
+
} else {
|
|
3756
|
+
const lintResult = await runLint({ vault, days: 90, lines: 200, logThreshold: 500 });
|
|
3757
|
+
if (lintResult.result.ok) {
|
|
3758
|
+
const fullErrors = lintResult.result.data.summary.errors;
|
|
3759
|
+
delta = { full_errors: fullErrors, base_errors: 0, new_errors: fullErrors, resolved_errors: 0 };
|
|
3760
|
+
if (fullErrors > 0) {
|
|
3761
|
+
const buckets = "by_severity" in lintResult.result.data ? lintResult.result.data.by_severity.error : [];
|
|
3762
|
+
return {
|
|
3763
|
+
exitCode: ExitCode.LINT_HAS_ERRORS,
|
|
3764
|
+
result: err("LINT_ERRORS_BLOCK_PUSH", {
|
|
3765
|
+
errors: fullErrors,
|
|
3766
|
+
buckets,
|
|
3767
|
+
message: "no origin base ref for delta; absolute lint errors block push"
|
|
3768
|
+
})
|
|
3769
|
+
};
|
|
3770
|
+
}
|
|
3771
|
+
} else {
|
|
3772
|
+
delta = { full_errors: 0, base_errors: 0, new_errors: 0, resolved_errors: 0 };
|
|
3773
|
+
}
|
|
3737
3774
|
}
|
|
3738
3775
|
try {
|
|
3739
3776
|
stageVaultContentChanges(vault);
|
|
@@ -3776,6 +3813,7 @@ async function runSyncPush(input) {
|
|
|
3776
3813
|
})
|
|
3777
3814
|
};
|
|
3778
3815
|
}
|
|
3816
|
+
const inheritedNote = delta.full_errors > 0 ? `; lint full=${delta.full_errors} base=${delta.base_errors} new=${delta.new_errors} resolved=${delta.resolved_errors} (inherited debt only)` : `; lint full=0 new=0`;
|
|
3779
3817
|
return {
|
|
3780
3818
|
exitCode: ExitCode.OK,
|
|
3781
3819
|
result: ok({
|
|
@@ -3783,7 +3821,11 @@ async function runSyncPush(input) {
|
|
|
3783
3821
|
commit_message: commitMessage,
|
|
3784
3822
|
pushed,
|
|
3785
3823
|
path_fixes: pathFixes,
|
|
3786
|
-
|
|
3824
|
+
lint_full_errors: delta.full_errors,
|
|
3825
|
+
lint_base_errors: delta.base_errors,
|
|
3826
|
+
lint_new_errors: delta.new_errors,
|
|
3827
|
+
lint_resolved_errors: delta.resolved_errors,
|
|
3828
|
+
humanHint: `committed and pushed ${dirtyFiles.length} file(s)${pathFixes > 0 ? ` after ${pathFixes} long-path fix(es)` : ""}${inheritedNote}`
|
|
3787
3829
|
})
|
|
3788
3830
|
};
|
|
3789
3831
|
}
|
|
@@ -5498,6 +5540,11 @@ syncCmd.command("peers [vault]").description("list active locks and recent wiki-
|
|
|
5498
5540
|
if (!v.ok) emit({ exitCode: v.exitCode, result: v.payload });
|
|
5499
5541
|
else emit(runSyncPeers({ vault: v.vault, sessionId: getCliSessionId() }));
|
|
5500
5542
|
});
|
|
5543
|
+
syncCmd.command("lint-delta [vault]").description("compare lint errors against a base ref; block only on new errors").option("--base-ref <ref>", "base git ref to compare against", "origin/main").option("--wiki <name>", "wiki profile name").action(async (vault, opts) => {
|
|
5544
|
+
const v = await resolveVaultArg(vault, opts.wiki);
|
|
5545
|
+
if (!v.ok) emit({ exitCode: v.exitCode, result: v.payload });
|
|
5546
|
+
else emit(await runSyncLintDelta({ vault: v.vault, baseRef: opts.baseRef }));
|
|
5547
|
+
});
|
|
5501
5548
|
var backupCmd = program.command("backup").description("manage S3-compatible remote backup");
|
|
5502
5549
|
backupCmd.command("sync [vault]").description("sync vault to S3-compatible remote backup").option("--dry-run", "list actions without executing").option("--bucket <name>", "S3 bucket name").option("--endpoint <url>", "S3 endpoint URL").option("--region <region>", "S3 region", "us-east-1").option("--prune", "delete orphaned S3 objects not in vault", false).option("--wiki <name>", "wiki profile name").action(async (vault, opts) => {
|
|
5503
5550
|
const v = await resolveVaultArg(vault, opts.wiki);
|
package/dist/skillwiki-mcp.js
CHANGED
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "skillwiki",
|
|
3
|
-
"version": "0.9.
|
|
3
|
+
"version": "0.9.58",
|
|
4
4
|
"skills": "./",
|
|
5
5
|
"description": "Project-aware Karpathy-style knowledge base for Claude Code: 18 prompt-only skills (wiki-*, proj-*, using-skillwiki) backed by the deterministic `skillwiki` CLI.",
|
|
6
6
|
"author": {
|
package/skills/README.md
CHANGED
|
@@ -1,11 +1,22 @@
|
|
|
1
1
|
# @skillwiki/skills
|
|
2
2
|
|
|
3
|
-
Prompt-only Markdown skills for Claude Code. Installed via `skillwiki install
|
|
3
|
+
Prompt-only Markdown skills for Claude Code. Installed via `skillwiki install`
|
|
4
|
+
or the Claude/Codex/Antigravity plugin packaging paths.
|
|
5
|
+
|
|
6
|
+
Current package inventory: **18 skills**.
|
|
4
7
|
|
|
5
8
|
| Namespace | Skills |
|
|
6
9
|
|---|---|
|
|
7
|
-
| `wiki-*` | `wiki-init`, `wiki-ingest`, `wiki-query`, `wiki-lint`, `wiki-crystallize`, `wiki-audit` |
|
|
10
|
+
| `wiki-*` | `wiki-init`, `wiki-ingest`, `wiki-query`, `wiki-lint`, `wiki-crystallize`, `wiki-audit`, `wiki-archive`, `wiki-reingest`, `wiki-adapter-prd`, `wiki-add-task`, `wiki-sync`, `wiki-canvas`, `wiki-gate-plan-mode` |
|
|
8
11
|
| `proj-*` | `proj-init`, `proj-work`, `proj-distill`, `proj-decide` |
|
|
12
|
+
| onboarding | `using-skillwiki` |
|
|
13
|
+
|
|
14
|
+
Verify the live inventory from source:
|
|
15
|
+
|
|
16
|
+
```bash
|
|
17
|
+
find packages/skills -mindepth 2 -maxdepth 2 -name SKILL.md -print | sort
|
|
18
|
+
bash scripts/verify-manifests.sh
|
|
19
|
+
```
|
|
9
20
|
|
|
10
21
|
Each top-level skill subdirectory holds one canonical `SKILL.md`. The nested
|
|
11
22
|
`skills/<skill>/SKILL.md` tree mirrors those files for Codex plugin discovery;
|
|
@@ -20,3 +31,6 @@ Codex-specific hook files. That root exposes `hooks/hooks-codex.json` and
|
|
|
20
31
|
Run `npm run materialize:plugins` from the repository root after changing
|
|
21
32
|
canonical skill, agent, or hook assets. Run
|
|
22
33
|
`npm run materialize:plugins:check` for read-only drift detection.
|
|
34
|
+
|
|
35
|
+
The sibling `vault-sync` plugin ships six additional operational skills under
|
|
36
|
+
`packages/vault-sync/skills/` and is packaged separately from this skill set.
|
package/skills/package.json
CHANGED
|
@@ -44,8 +44,8 @@ On every invocation, count `wiki-sync:*` stashes older than 24 hours via `skillw
|
|
|
44
44
|
|
|
45
45
|
### Push workflow
|
|
46
46
|
4. If vault is dirty, ask the user to review uncommitted changes before proceeding.
|
|
47
|
-
5. Run `skillwiki lint <vault
|
|
48
|
-
6. If lint
|
|
47
|
+
5. Run `skillwiki sync lint-delta <vault> --base-ref origin/main`. Block only when `new_errors > 0`. Report full/base/new/resolved. Malformed delta evidence fails closed — do not push.
|
|
48
|
+
6. If lint-delta allows (new_errors = 0), stage and commit:
|
|
49
49
|
- `git -C <vault> add -A`
|
|
50
50
|
- `git -C <vault> commit -m "sync: vault update $(date -u +%Y-%m-%dT%H:%MZ)"`
|
|
51
51
|
7. Run `git -C <vault> push origin HEAD`. Report result.
|
|
@@ -62,7 +62,7 @@ On every invocation, count `wiki-sync:*` stashes older than 24 hours via `skillw
|
|
|
62
62
|
MSG="wiki-sync:${SESSION_ID}:${CWD_HASH}:${ISO_TS}:pre-pull"
|
|
63
63
|
git -C "$VAULT" stash push -m "$MSG"
|
|
64
64
|
```
|
|
65
|
-
11.
|
|
65
|
+
11. Prefer the canonical pull helper (`wiki-pull-with-auto-resolve.sh` / vault-presync `--execute`) so stale-clean rebase state uses recovery-ref + `rebase --quit`, active rebases fail closed, and only fully materialized commits are dropped. If invoking git directly: `git -C <vault> pull --rebase origin HEAD`. Report result.
|
|
66
66
|
12. If a stash was created, pop it: `git -C <vault> stash pop`.
|
|
67
67
|
13. If conflicts occur during stash pop, identify them and present to the user for resolution (see Conflict Resolution below).
|
|
68
68
|
14. Run `skillwiki lint <vault>` after pull to verify vault integrity.
|
|
@@ -240,3 +240,20 @@ bash ~/.hermes/scripts/wiki-snapshot.sh # Re-sync fresh
|
|
|
240
240
|
- Modifying files in `raw/` to resolve conflicts (N9 — archive and re-ingest instead).
|
|
241
241
|
- Stashing without the `wiki-sync:...` name format (breaks peer detection).
|
|
242
242
|
- Force-deleting a peer's lockfile (use `--force` only if peer is confirmed dead).
|
|
243
|
+
|
|
244
|
+
## Convergence safeguards (2026-07-11)
|
|
245
|
+
|
|
246
|
+
### Rebase-state classification
|
|
247
|
+
- `stale-clean`: recovery ref at tip + `git rebase --quit` (preserves advanced tip; never abort-reset to orig-head).
|
|
248
|
+
- `active` (REBASE_HEAD / UU paths): leave untouched; fail closed.
|
|
249
|
+
- Recovery refs live under `refs/vault-sync/recovery/<UTC timestamp>`.
|
|
250
|
+
|
|
251
|
+
### Materialized-commit proof
|
|
252
|
+
Drop a local commit from rebase only when every path is proven present on the target ref (exact blobs; byte-identical added `## ` log sections). Partial/raw/rename mismatches retain or stop.
|
|
253
|
+
|
|
254
|
+
### Lint-delta fail-closed
|
|
255
|
+
- Fingerprints: `<bucket>\0<page>\0<normalized-detail>`
|
|
256
|
+
- CLI: `skillwiki sync lint-delta <vault> --base-ref origin/main`
|
|
257
|
+
- Block publication only when `new_errors > 0`; inherited full debt remains visible.
|
|
258
|
+
- Missing/malformed delta evidence blocks (never silent lint skip).
|
|
259
|
+
|
|
@@ -44,8 +44,8 @@ On every invocation, count `wiki-sync:*` stashes older than 24 hours via `skillw
|
|
|
44
44
|
|
|
45
45
|
### Push workflow
|
|
46
46
|
4. If vault is dirty, ask the user to review uncommitted changes before proceeding.
|
|
47
|
-
5. Run `skillwiki lint <vault
|
|
48
|
-
6. If lint
|
|
47
|
+
5. Run `skillwiki sync lint-delta <vault> --base-ref origin/main`. Block only when `new_errors > 0`. Report full/base/new/resolved. Malformed delta evidence fails closed — do not push.
|
|
48
|
+
6. If lint-delta allows (new_errors = 0), stage and commit:
|
|
49
49
|
- `git -C <vault> add -A`
|
|
50
50
|
- `git -C <vault> commit -m "sync: vault update $(date -u +%Y-%m-%dT%H:%MZ)"`
|
|
51
51
|
7. Run `git -C <vault> push origin HEAD`. Report result.
|
|
@@ -62,7 +62,7 @@ On every invocation, count `wiki-sync:*` stashes older than 24 hours via `skillw
|
|
|
62
62
|
MSG="wiki-sync:${SESSION_ID}:${CWD_HASH}:${ISO_TS}:pre-pull"
|
|
63
63
|
git -C "$VAULT" stash push -m "$MSG"
|
|
64
64
|
```
|
|
65
|
-
11.
|
|
65
|
+
11. Prefer the canonical pull helper (`wiki-pull-with-auto-resolve.sh` / vault-presync `--execute`) so stale-clean rebase state uses recovery-ref + `rebase --quit`, active rebases fail closed, and only fully materialized commits are dropped. If invoking git directly: `git -C <vault> pull --rebase origin HEAD`. Report result.
|
|
66
66
|
12. If a stash was created, pop it: `git -C <vault> stash pop`.
|
|
67
67
|
13. If conflicts occur during stash pop, identify them and present to the user for resolution (see Conflict Resolution below).
|
|
68
68
|
14. Run `skillwiki lint <vault>` after pull to verify vault integrity.
|
|
@@ -240,3 +240,20 @@ bash ~/.hermes/scripts/wiki-snapshot.sh # Re-sync fresh
|
|
|
240
240
|
- Modifying files in `raw/` to resolve conflicts (N9 — archive and re-ingest instead).
|
|
241
241
|
- Stashing without the `wiki-sync:...` name format (breaks peer detection).
|
|
242
242
|
- Force-deleting a peer's lockfile (use `--force` only if peer is confirmed dead).
|
|
243
|
+
|
|
244
|
+
## Convergence safeguards (2026-07-11)
|
|
245
|
+
|
|
246
|
+
### Rebase-state classification
|
|
247
|
+
- `stale-clean`: recovery ref at tip + `git rebase --quit` (preserves advanced tip; never abort-reset to orig-head).
|
|
248
|
+
- `active` (REBASE_HEAD / UU paths): leave untouched; fail closed.
|
|
249
|
+
- Recovery refs live under `refs/vault-sync/recovery/<UTC timestamp>`.
|
|
250
|
+
|
|
251
|
+
### Materialized-commit proof
|
|
252
|
+
Drop a local commit from rebase only when every path is proven present on the target ref (exact blobs; byte-identical added `## ` log sections). Partial/raw/rename mismatches retain or stop.
|
|
253
|
+
|
|
254
|
+
### Lint-delta fail-closed
|
|
255
|
+
- Fingerprints: `<bucket>\0<page>\0<normalized-detail>`
|
|
256
|
+
- CLI: `skillwiki sync lint-delta <vault> --base-ref origin/main`
|
|
257
|
+
- Block publication only when `new_errors > 0`; inherited full debt remains visible.
|
|
258
|
+
- Missing/malformed delta evidence blocks (never silent lint skip).
|
|
259
|
+
|