knodin 0.7.4 → 0.7.6
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 +4 -1
- package/dist/bin/cli.js +108 -1859
- package/dist/bin/launcher.js +3 -10
- package/dist/chunks/chunk-DMQAGX77.js +654 -0
- package/dist/chunks/chunk-F4Z3Z766.js +4 -0
- package/dist/chunks/chunk-SIJAQVSX.js +3 -0
- package/dist/chunks/chunk-X6M4HUUE.js +2 -0
- package/dist/chunks/chunk-YPRMY2LP.js +8 -0
- package/dist/chunks/pure-compression-cli-4TA2TQD5.js +5 -0
- package/dist/chunks/server-7EDF4CBY.js +14 -0
- package/dist/chunks/structural-fast-path-KD5KQSPX.js +4 -0
- package/dist/src/init-progress-worker.js +2 -106
- package/dist/src/repair-progress-worker.js +2 -120
- package/docs/INSTALLATION.md +17 -1
- package/docs/releases/0.7.6.md +25 -0
- package/package.json +1 -110
- package/dist/src/agent-integration.js +0 -238
- package/dist/src/artifact-refresh.js +0 -81
- package/dist/src/cli-args.js +0 -274
- package/dist/src/cli-model.js +0 -358
- package/dist/src/codeflow-replay.js +0 -80
- package/dist/src/compact-structural.js +0 -96
- package/dist/src/competitive-cold-mcp.js +0 -40
- package/dist/src/competitive-constraints.js +0 -20
- package/dist/src/competitive-manifest.js +0 -411
- package/dist/src/competitive-measurement.js +0 -183
- package/dist/src/competitive-runner.js +0 -487
- package/dist/src/competitive-sandbox.js +0 -108
- package/dist/src/context-export.js +0 -422
- package/dist/src/context.js +0 -102
- package/dist/src/diagnostics.js +0 -449
- package/dist/src/docs-sections.js +0 -141
- package/dist/src/doctor.js +0 -380
- package/dist/src/engine/ann-hnsw.js +0 -271
- package/dist/src/engine/embeddings.js +0 -193
- package/dist/src/engine/file-walker.js +0 -43
- package/dist/src/engine/git-history.js +0 -289
- package/dist/src/engine/index.js +0 -13377
- package/dist/src/engine/perf.js +0 -115
- package/dist/src/engine/prune.js +0 -112
- package/dist/src/engine/scip-import.js +0 -408
- package/dist/src/engine/source-policy.js +0 -69
- package/dist/src/engine/sqlite.js +0 -71
- package/dist/src/engine/symbol-delete.js +0 -58
- package/dist/src/execution-profile.js +0 -203
- package/dist/src/failure-diagnosis.js +0 -649
- package/dist/src/fleet.js +0 -7
- package/dist/src/git-executable.js +0 -31
- package/dist/src/graph-query-health.js +0 -115
- package/dist/src/hook-manager-integration.js +0 -156
- package/dist/src/index-activity.js +0 -125
- package/dist/src/init-progress.js +0 -155
- package/dist/src/init.js +0 -1254
- package/dist/src/lifecycle-health.js +0 -250
- package/dist/src/lsp-readonly.js +0 -217
- package/dist/src/output-compression.js +0 -629
- package/dist/src/output-telemetry.js +0 -363
- package/dist/src/pr-triage.js +0 -638
- package/dist/src/progressive-evidence.js +0 -473
- package/dist/src/pure-compression-cli.js +0 -101
- package/dist/src/relationship-adapters.js +0 -370
- package/dist/src/release-attestation.js +0 -533
- package/dist/src/release-preflight.js +0 -510
- package/dist/src/repair-progress.js +0 -262
- package/dist/src/repository-init-process.js +0 -173
- package/dist/src/repository-management.js +0 -1231
- package/dist/src/response-budget.js +0 -194
- package/dist/src/server.js +0 -73
- package/dist/src/structural-fast-path.js +0 -338
- package/dist/src/structural-snapshot.js +0 -33
- package/dist/src/system-config.js +0 -615
- package/dist/src/terminal-help.js +0 -83
- package/dist/src/tools/knodin-tools.js +0 -1529
- package/dist/src/update-ceremony.js +0 -158
- package/dist/src/update-policy.js +0 -944
- package/dist/src/update-trust.js +0 -503
- package/dist/src/version.js +0 -13
- package/dist/src/visualization.js +0 -162
- package/dist/src/wait-for-fresh.js +0 -98
- package/dist/src/worktree-lifecycle.js +0 -231
- package/docs/CONTAINED-EXECUTION.md +0 -77
- package/docs/DIAGNOSTICS.md +0 -45
- package/docs/GIT-HISTORY-REVIEW.md +0 -39
- package/docs/PROGRESSIVE-EVIDENCE.md +0 -37
- package/docs/PT-ACCESS-RECOMMENDATION.md +0 -91
- package/docs/RELEASE-0.3-EVIDENCE.md +0 -73
- package/docs/SCIP-IMPORT.md +0 -57
- package/docs/SIGNED-UPDATES.md +0 -151
- package/docs/TELEMETRY.md +0 -46
- package/docs/assets/knodin-favicon.svg +0 -4
- package/docs/releases/0.3.0.md +0 -46
- package/docs/releases/0.4.0.md +0 -68
- package/docs/releases/0.4.1.md +0 -28
- package/docs/releases/0.4.2.md +0 -27
- package/docs/releases/0.4.3.md +0 -23
- package/docs/releases/0.5.0.md +0 -29
- package/docs/releases/0.5.1.md +0 -17
- package/docs/releases/0.6.0.md +0 -18
- package/docs/releases/0.7.0.md +0 -24
- package/docs/releases/0.7.1.md +0 -21
- package/docs/releases/0.7.2.md +0 -21
- package/docs/releases/0.7.3.md +0 -23
- package/docs/releases/0.7.4.md +0 -17
- package/schemas/release-attestation-v1.schema.json +0 -210
|
@@ -1,98 +0,0 @@
|
|
|
1
|
-
import childProcess from "node:child_process";
|
|
2
|
-
import fs from "node:fs";
|
|
3
|
-
import path from "node:path";
|
|
4
|
-
import { attachLifecycleHealth, inspectLifecycleHealth } from "./lifecycle-health.js";
|
|
5
|
-
const delay = (milliseconds) => new Promise((resolve) => setTimeout(resolve, milliseconds));
|
|
6
|
-
const PROCESSOR_RETRY_MS = 500;
|
|
7
|
-
function installedProcessorIsRunning(repo) {
|
|
8
|
-
try {
|
|
9
|
-
const pid = Number(fs.readFileSync(path.join(repo, ".knodin", "hooks", "refresh.lock", "pid"), "utf-8"));
|
|
10
|
-
if (!Number.isInteger(pid) || pid <= 0)
|
|
11
|
-
return false;
|
|
12
|
-
process.kill(pid, 0);
|
|
13
|
-
return true;
|
|
14
|
-
}
|
|
15
|
-
catch {
|
|
16
|
-
return false;
|
|
17
|
-
}
|
|
18
|
-
}
|
|
19
|
-
export function launchInstalledLifecycleProcessor(repo) {
|
|
20
|
-
const processor = path.join(repo, ".knodin", "hooks", "background-index.sh");
|
|
21
|
-
if (fs.statSync(processor).mode & 0o111) {
|
|
22
|
-
const child = childProcess.spawn(processor, [], {
|
|
23
|
-
cwd: repo,
|
|
24
|
-
stdio: "ignore",
|
|
25
|
-
detached: process.platform !== "win32",
|
|
26
|
-
windowsHide: true,
|
|
27
|
-
});
|
|
28
|
-
child.once("error", () => undefined);
|
|
29
|
-
child.unref();
|
|
30
|
-
}
|
|
31
|
-
}
|
|
32
|
-
function requiresStructuralRepair(graph) {
|
|
33
|
-
if (Object.values(graph.orphaned).some((count) => count > 0))
|
|
34
|
-
return true;
|
|
35
|
-
const ordinary = [
|
|
36
|
-
"indexed file no longer exists: ",
|
|
37
|
-
"indexed file is no longer eligible: ",
|
|
38
|
-
"indexed snapshot differs from disk: ",
|
|
39
|
-
];
|
|
40
|
-
return graph.missing.records.some((record) => !ordinary.some((prefix) => record.startsWith(prefix)));
|
|
41
|
-
}
|
|
42
|
-
function launchQueuedProcessorIfNeeded(repo, elapsed, timeoutMs, lastAttemptAt, processQueuedEvents) {
|
|
43
|
-
if (elapsed >= timeoutMs ||
|
|
44
|
-
inspectLifecycleHealth(repo).queuedEvents === 0 ||
|
|
45
|
-
installedProcessorIsRunning(repo) ||
|
|
46
|
-
elapsed - lastAttemptAt < PROCESSOR_RETRY_MS)
|
|
47
|
-
return lastAttemptAt;
|
|
48
|
-
try {
|
|
49
|
-
processQueuedEvents(repo);
|
|
50
|
-
}
|
|
51
|
-
catch {
|
|
52
|
-
// Lifecycle health retains the queued event and wait remains truthful.
|
|
53
|
-
}
|
|
54
|
-
return elapsed;
|
|
55
|
-
}
|
|
56
|
-
function completedStatus(graph) {
|
|
57
|
-
if (graph.status === "healthy" && graph.freshness.state === "fresh")
|
|
58
|
-
return "fresh";
|
|
59
|
-
if (graph.status === "repair-needed" && requiresStructuralRepair(graph))
|
|
60
|
-
return "repair-needed";
|
|
61
|
-
if (graph.freshness.state === "unknown")
|
|
62
|
-
return "unknown";
|
|
63
|
-
return null;
|
|
64
|
-
}
|
|
65
|
-
async function reconcileOrdinaryDrift(engine, repo, graph) {
|
|
66
|
-
const shouldReconcile = (graph.status === "stale" || graph.status === "repair-needed") &&
|
|
67
|
-
graph.freshness.state !== "queued";
|
|
68
|
-
if (!shouldReconcile)
|
|
69
|
-
return graph;
|
|
70
|
-
await engine.query("stats", "", repo);
|
|
71
|
-
return attachLifecycleHealth(repo, await engine.status(repo, { audit: "deep" }));
|
|
72
|
-
}
|
|
73
|
-
function completedResult(status, startedAt, polls, graph) {
|
|
74
|
-
return { status, waitedMs: Date.now() - startedAt, polls, graph };
|
|
75
|
-
}
|
|
76
|
-
/** Wait for queued work and reconcile ordinary drift; structural repair stays explicit. */
|
|
77
|
-
export async function waitForFresh(engine, repo, timeoutMs = 30_000, processQueuedEvents = launchInstalledLifecycleProcessor) {
|
|
78
|
-
const startedAt = Date.now();
|
|
79
|
-
let polls = 0;
|
|
80
|
-
let lastProcessorAttemptAt = Number.NEGATIVE_INFINITY;
|
|
81
|
-
for (;;) {
|
|
82
|
-
polls++;
|
|
83
|
-
const beforeStatusElapsed = Date.now() - startedAt;
|
|
84
|
-
lastProcessorAttemptAt = launchQueuedProcessorIfNeeded(repo, beforeStatusElapsed, timeoutMs, lastProcessorAttemptAt, processQueuedEvents);
|
|
85
|
-
let graph = attachLifecycleHealth(repo, await engine.status(repo, { audit: "cached" }));
|
|
86
|
-
let status = completedStatus(graph);
|
|
87
|
-
if (status)
|
|
88
|
-
return completedResult(status, startedAt, polls, graph);
|
|
89
|
-
graph = await reconcileOrdinaryDrift(engine, repo, graph);
|
|
90
|
-
status = completedStatus(graph);
|
|
91
|
-
if (status)
|
|
92
|
-
return completedResult(status, startedAt, polls, graph);
|
|
93
|
-
const elapsed = Date.now() - startedAt;
|
|
94
|
-
if (elapsed >= timeoutMs)
|
|
95
|
-
return { status: "timeout", waitedMs: elapsed, polls, graph };
|
|
96
|
-
await delay(Math.min(100, timeoutMs - elapsed));
|
|
97
|
-
}
|
|
98
|
-
}
|
|
@@ -1,231 +0,0 @@
|
|
|
1
|
-
import childProcess from "node:child_process";
|
|
2
|
-
import crypto from "node:crypto";
|
|
3
|
-
import fs from "node:fs";
|
|
4
|
-
import path from "node:path";
|
|
5
|
-
function git(repo, args) {
|
|
6
|
-
return childProcess.execFileSync("git", args, {
|
|
7
|
-
cwd: repo,
|
|
8
|
-
encoding: "utf-8",
|
|
9
|
-
stdio: ["ignore", "pipe", "pipe"],
|
|
10
|
-
});
|
|
11
|
-
}
|
|
12
|
-
function canonicalPath(target) {
|
|
13
|
-
try {
|
|
14
|
-
return fs.realpathSync(target);
|
|
15
|
-
}
|
|
16
|
-
catch {
|
|
17
|
-
return path.resolve(target);
|
|
18
|
-
}
|
|
19
|
-
}
|
|
20
|
-
function commonDirectory(repo) {
|
|
21
|
-
const value = git(repo, ["rev-parse", "--path-format=absolute", "--git-common-dir"]).trim();
|
|
22
|
-
return path.resolve(repo, value);
|
|
23
|
-
}
|
|
24
|
-
function registryPath(repo) {
|
|
25
|
-
return path.join(commonDirectory(repo), "knodin", "worktrees.json");
|
|
26
|
-
}
|
|
27
|
-
function emptyRegistry() {
|
|
28
|
-
return { schemaVersion: 1, graphState: "per-worktree", entries: [] };
|
|
29
|
-
}
|
|
30
|
-
function readRegistry(repo) {
|
|
31
|
-
try {
|
|
32
|
-
const parsed = JSON.parse(fs.readFileSync(registryPath(repo), "utf-8"));
|
|
33
|
-
return parsed.schemaVersion === 1 && Array.isArray(parsed.entries) ? parsed : emptyRegistry();
|
|
34
|
-
}
|
|
35
|
-
catch {
|
|
36
|
-
return emptyRegistry();
|
|
37
|
-
}
|
|
38
|
-
}
|
|
39
|
-
function writeRegistry(repo, registry) {
|
|
40
|
-
const target = registryPath(repo);
|
|
41
|
-
fs.mkdirSync(path.dirname(target), { recursive: true });
|
|
42
|
-
const temporary = `${target}.${process.pid}.tmp`;
|
|
43
|
-
fs.writeFileSync(temporary, `${JSON.stringify(registry, null, 2)}\n`, { mode: 0o600 });
|
|
44
|
-
fs.renameSync(temporary, target);
|
|
45
|
-
}
|
|
46
|
-
function identityFor(repo, worktreePath, classification) {
|
|
47
|
-
let administrativePath = classification;
|
|
48
|
-
try {
|
|
49
|
-
administrativePath = path.resolve(worktreePath, git(worktreePath, ["rev-parse", "--path-format=absolute", "--git-dir"]).trim());
|
|
50
|
-
}
|
|
51
|
-
catch {
|
|
52
|
-
// A removed worktree retains its registry identity.
|
|
53
|
-
}
|
|
54
|
-
return crypto
|
|
55
|
-
.createHash("sha256")
|
|
56
|
-
.update(`${commonDirectory(repo)}\0${administrativePath}`)
|
|
57
|
-
.digest("hex")
|
|
58
|
-
.slice(0, 24);
|
|
59
|
-
}
|
|
60
|
-
function listGitWorktrees(repo) {
|
|
61
|
-
const fields = git(repo, ["worktree", "list", "--porcelain", "-z"]).split("\0");
|
|
62
|
-
const records = [];
|
|
63
|
-
for (const field of fields) {
|
|
64
|
-
if (field.startsWith("worktree ")) {
|
|
65
|
-
records.push({
|
|
66
|
-
path: path.resolve(field.slice("worktree ".length)),
|
|
67
|
-
classification: records.length === 0 ? "main" : "linked",
|
|
68
|
-
head: null,
|
|
69
|
-
branch: null,
|
|
70
|
-
locked: false,
|
|
71
|
-
prunable: null,
|
|
72
|
-
});
|
|
73
|
-
continue;
|
|
74
|
-
}
|
|
75
|
-
const current = records.at(-1);
|
|
76
|
-
if (!current)
|
|
77
|
-
continue;
|
|
78
|
-
if (field.startsWith("HEAD "))
|
|
79
|
-
current.head = field.slice(5);
|
|
80
|
-
else if (field.startsWith("branch "))
|
|
81
|
-
current.branch = field.slice(7).replace(/^refs\/heads\//, "");
|
|
82
|
-
else if (field === "detached")
|
|
83
|
-
current.branch = null;
|
|
84
|
-
else if (field.startsWith("locked"))
|
|
85
|
-
current.locked = true;
|
|
86
|
-
else if (field.startsWith("prunable "))
|
|
87
|
-
current.prunable = field.slice(9);
|
|
88
|
-
}
|
|
89
|
-
return records;
|
|
90
|
-
}
|
|
91
|
-
/** Register one initialized checkout in Git-common metadata without sharing its graph database. */
|
|
92
|
-
export function registerInitializedWorktree(repoPath) {
|
|
93
|
-
const repo = path.resolve(repoPath);
|
|
94
|
-
const current = listGitWorktrees(repo).find((worktree) => canonicalPath(worktree.path) === canonicalPath(repo));
|
|
95
|
-
if (!current)
|
|
96
|
-
throw new Error(`knodin worktrees: ${repo} is not a registered Git worktree`);
|
|
97
|
-
const registry = readRegistry(repo);
|
|
98
|
-
const now = new Date().toISOString();
|
|
99
|
-
const identity = identityFor(repo, current.path, current.classification);
|
|
100
|
-
const existing = registry.entries.find((entry) => entry.identity === identity);
|
|
101
|
-
if (existing) {
|
|
102
|
-
existing.path = current.path;
|
|
103
|
-
existing.lastSeenAt = now;
|
|
104
|
-
delete existing.removedAt;
|
|
105
|
-
}
|
|
106
|
-
else {
|
|
107
|
-
registry.entries.push({
|
|
108
|
-
identity,
|
|
109
|
-
path: current.path,
|
|
110
|
-
classification: current.classification,
|
|
111
|
-
registeredAt: now,
|
|
112
|
-
lastSeenAt: now,
|
|
113
|
-
});
|
|
114
|
-
}
|
|
115
|
-
registry.entries.sort((left, right) => left.path.localeCompare(right.path));
|
|
116
|
-
writeRegistry(repo, registry);
|
|
117
|
-
}
|
|
118
|
-
export async function inspectWorktrees(repoPath, status) {
|
|
119
|
-
const repo = path.resolve(repoPath);
|
|
120
|
-
const current = listGitWorktrees(repo);
|
|
121
|
-
const registry = readRegistry(repo);
|
|
122
|
-
const livePaths = new Set(current.map((worktree) => worktree.path));
|
|
123
|
-
const worktrees = [];
|
|
124
|
-
for (const worktree of current) {
|
|
125
|
-
const identity = identityFor(repo, worktree.path, worktree.classification);
|
|
126
|
-
const registered = registry.entries.some((entry) => entry.identity === identity);
|
|
127
|
-
const graphInitialized = fs.existsSync(path.join(worktree.path, ".knodin", "db.sqlite"));
|
|
128
|
-
let indexedHead = null;
|
|
129
|
-
if (graphInitialized) {
|
|
130
|
-
try {
|
|
131
|
-
indexedHead = (await status(worktree.path)).lastIndexedHead || null;
|
|
132
|
-
}
|
|
133
|
-
catch {
|
|
134
|
-
indexedHead = null;
|
|
135
|
-
}
|
|
136
|
-
}
|
|
137
|
-
worktrees.push({
|
|
138
|
-
...worktree,
|
|
139
|
-
identity,
|
|
140
|
-
registered,
|
|
141
|
-
graphInitialized,
|
|
142
|
-
indexedHead,
|
|
143
|
-
freshness: indexedHead === null || worktree.head === null
|
|
144
|
-
? "unknown"
|
|
145
|
-
: indexedHead === worktree.head
|
|
146
|
-
? "fresh"
|
|
147
|
-
: "stale",
|
|
148
|
-
removedAt: null,
|
|
149
|
-
});
|
|
150
|
-
}
|
|
151
|
-
for (const entry of registry.entries) {
|
|
152
|
-
if (livePaths.has(entry.path))
|
|
153
|
-
continue;
|
|
154
|
-
worktrees.push({
|
|
155
|
-
identity: entry.identity,
|
|
156
|
-
path: entry.path,
|
|
157
|
-
classification: entry.classification,
|
|
158
|
-
head: null,
|
|
159
|
-
branch: null,
|
|
160
|
-
locked: false,
|
|
161
|
-
prunable: "registered worktree is absent from Git's worktree inventory",
|
|
162
|
-
registered: true,
|
|
163
|
-
graphInitialized: fs.existsSync(path.join(entry.path, ".knodin", "db.sqlite")),
|
|
164
|
-
indexedHead: null,
|
|
165
|
-
freshness: "removed",
|
|
166
|
-
removedAt: entry.removedAt ?? null,
|
|
167
|
-
});
|
|
168
|
-
}
|
|
169
|
-
worktrees.sort((left, right) => left.path.localeCompare(right.path));
|
|
170
|
-
return { schemaVersion: 1, graphState: "per-worktree", worktrees };
|
|
171
|
-
}
|
|
172
|
-
/** Reconcile the durable registry with Git metadata; no checkout or graph is deleted. */
|
|
173
|
-
export function reconcileWorktrees(repoPath) {
|
|
174
|
-
const repo = path.resolve(repoPath);
|
|
175
|
-
const current = listGitWorktrees(repo);
|
|
176
|
-
const registry = readRegistry(repo);
|
|
177
|
-
const now = new Date().toISOString();
|
|
178
|
-
const seen = new Set();
|
|
179
|
-
const added = [];
|
|
180
|
-
const restored = [];
|
|
181
|
-
for (const worktree of current) {
|
|
182
|
-
const identity = identityFor(repo, worktree.path, worktree.classification);
|
|
183
|
-
seen.add(identity);
|
|
184
|
-
const existing = registry.entries.find((entry) => entry.identity === identity);
|
|
185
|
-
if (!existing) {
|
|
186
|
-
registry.entries.push({
|
|
187
|
-
identity,
|
|
188
|
-
path: worktree.path,
|
|
189
|
-
classification: worktree.classification,
|
|
190
|
-
registeredAt: now,
|
|
191
|
-
lastSeenAt: now,
|
|
192
|
-
});
|
|
193
|
-
added.push(worktree.path);
|
|
194
|
-
}
|
|
195
|
-
else {
|
|
196
|
-
if (existing.removedAt)
|
|
197
|
-
restored.push(worktree.path);
|
|
198
|
-
existing.path = worktree.path;
|
|
199
|
-
existing.lastSeenAt = now;
|
|
200
|
-
delete existing.removedAt;
|
|
201
|
-
}
|
|
202
|
-
}
|
|
203
|
-
const removed = [];
|
|
204
|
-
for (const entry of registry.entries) {
|
|
205
|
-
if (!seen.has(entry.identity) && !entry.removedAt) {
|
|
206
|
-
entry.removedAt = now;
|
|
207
|
-
removed.push(entry.path);
|
|
208
|
-
}
|
|
209
|
-
}
|
|
210
|
-
registry.entries.sort((left, right) => left.path.localeCompare(right.path));
|
|
211
|
-
writeRegistry(repo, registry);
|
|
212
|
-
return { schemaVersion: 1, added, restored, removed };
|
|
213
|
-
}
|
|
214
|
-
/** Remove exactly one linked worktree through Git, then retain removal evidence in the registry. */
|
|
215
|
-
export function removeManagedWorktree(repoPath, targetPath, dryRun) {
|
|
216
|
-
const repo = path.resolve(repoPath);
|
|
217
|
-
const target = path.resolve(targetPath);
|
|
218
|
-
const record = listGitWorktrees(repo).find((worktree) => canonicalPath(worktree.path) === canonicalPath(target));
|
|
219
|
-
if (!record)
|
|
220
|
-
throw new Error(`knodin worktrees remove: ${target} is not a registered Git worktree`);
|
|
221
|
-
if (record.classification === "main")
|
|
222
|
-
throw new Error("knodin worktrees remove: refusing to remove the main worktree");
|
|
223
|
-
if (dryRun)
|
|
224
|
-
return { schemaVersion: 1, target, dryRun: true, removed: false };
|
|
225
|
-
childProcess.execFileSync("git", ["worktree", "remove", target], {
|
|
226
|
-
cwd: repo,
|
|
227
|
-
stdio: ["ignore", "pipe", "pipe"],
|
|
228
|
-
});
|
|
229
|
-
reconcileWorktrees(repo);
|
|
230
|
-
return { schemaVersion: 1, target, dryRun: false, removed: true };
|
|
231
|
-
}
|
|
@@ -1,77 +0,0 @@
|
|
|
1
|
-
# Profile-based contained execution
|
|
2
|
-
|
|
3
|
-
`knodin` can execute one immutable repository-defined profile through the
|
|
4
|
-
single MCP gateway. It never accepts a command string, executable, argv, cwd,
|
|
5
|
-
environment, or limit from an MCP caller.
|
|
6
|
-
|
|
7
|
-
Execution is disabled by two independent defaults. Enable it globally in
|
|
8
|
-
`$XDG_CONFIG_HOME/knodin/execution.json` (or
|
|
9
|
-
`~/.config/knodin/execution.json`):
|
|
10
|
-
|
|
11
|
-
```json
|
|
12
|
-
{"execution":{"enabled":true}}
|
|
13
|
-
```
|
|
14
|
-
|
|
15
|
-
Then define and enable exact profiles in `.knodin/execution.json`:
|
|
16
|
-
|
|
17
|
-
```json
|
|
18
|
-
{
|
|
19
|
-
"execution": {
|
|
20
|
-
"enabled": true,
|
|
21
|
-
"profiles": {
|
|
22
|
-
"test": {
|
|
23
|
-
"executable": "/opt/homebrew/bin/bun",
|
|
24
|
-
"args": ["run", "test"],
|
|
25
|
-
"network": "deny",
|
|
26
|
-
"timeoutMs": 120000,
|
|
27
|
-
"maxOutputBytes": 2097152,
|
|
28
|
-
"maxProcesses": 1,
|
|
29
|
-
"containment": "fully-contained"
|
|
30
|
-
}
|
|
31
|
-
}
|
|
32
|
-
}
|
|
33
|
-
}
|
|
34
|
-
```
|
|
35
|
-
|
|
36
|
-
An agent may call `{ "operation": "execute", "profile": "test" }`. Profile
|
|
37
|
-
names are bounded identifiers. Shell strings and caller-supplied argument
|
|
38
|
-
suffixes are not part of the schema.
|
|
39
|
-
|
|
40
|
-
## Containment contract
|
|
41
|
-
|
|
42
|
-
The initial certified adapter is macOS `sandbox-exec`. It runs with a minimal
|
|
43
|
-
environment (`PATH`, `LANG`, `LC_ALL`, and repository-local `TMPDIR`), closed
|
|
44
|
-
stdin, no shell, repository cwd, a detached process group, hard timeout and
|
|
45
|
-
combined-output caps, and Seatbelt rules that:
|
|
46
|
-
|
|
47
|
-
- deny all network operations;
|
|
48
|
-
- deny process forks, making `maxProcesses: 1` a hard contract;
|
|
49
|
-
- deny all writes outside the repository;
|
|
50
|
-
- deny reads under user homes, mounted volumes, network mounts, and shared
|
|
51
|
-
temporary roots outside the repository; and
|
|
52
|
-
- allow repository reads/writes and read-only system runtime paths required to
|
|
53
|
-
load the approved executable.
|
|
54
|
-
|
|
55
|
-
Profiles needing child processes are rejected by this first contract. Define
|
|
56
|
-
the profile around a single-process test/lint/typecheck/build executable. The
|
|
57
|
-
macOS adapter reports `fully-contained` only when every configured boundary is
|
|
58
|
-
active. It also reports that `sandbox-exec` is deprecated; this certification
|
|
59
|
-
is tied to the checked-in adversarial tests, not a claim that Seatbelt is a
|
|
60
|
-
supported public Apple API.
|
|
61
|
-
|
|
62
|
-
Linux and Windows currently report `containment-unavailable` and do not spawn
|
|
63
|
-
the profile. A future Bubblewrap or Windows Job Object/restricted-token adapter
|
|
64
|
-
requires its own native evidence before it can change that result.
|
|
65
|
-
|
|
66
|
-
## Output, diagnosis, and audit
|
|
67
|
-
|
|
68
|
-
Captured stdout/stderr goes directly through recoverable compression. Failed,
|
|
69
|
-
timed-out, and output-limited MCP executions then request source-evidenced
|
|
70
|
-
failure diagnosis from the existing graph. Raw command text, argv, environment,
|
|
71
|
-
and output are never written to execution telemetry. The private repository
|
|
72
|
-
audit `.knodin/execution-audit.jsonl` records only timestamp, profile identity,
|
|
73
|
-
status, exit/signal, reported containment, and retained output-artifact identity.
|
|
74
|
-
|
|
75
|
-
Repository code can still be destructive inside the authorized repository.
|
|
76
|
-
This is a constrained trusted-repository workflow, not a guarantee that an
|
|
77
|
-
approved test/build profile cannot modify its own checkout.
|
package/docs/DIAGNOSTICS.md
DELETED
|
@@ -1,45 +0,0 @@
|
|
|
1
|
-
# Local troubleshooting diagnostics
|
|
2
|
-
|
|
3
|
-
knodin can retain a small, local diagnostic journal and build a redacted bundle
|
|
4
|
-
for a user to inspect and share with support. Diagnostics are disabled until the
|
|
5
|
-
user enables them, never upload automatically, and require no account or hosted
|
|
6
|
-
service.
|
|
7
|
-
|
|
8
|
-
```bash
|
|
9
|
-
knodin diagnostics enable --retention-days 14
|
|
10
|
-
knodin diagnostics status
|
|
11
|
-
knodin diagnostics collect --since 24h
|
|
12
|
-
knodin diagnostics inspect .knodin/diagnostics/knodin-diagnostics-….json.gz
|
|
13
|
-
knodin diagnostics clear
|
|
14
|
-
knodin diagnostics disable
|
|
15
|
-
```
|
|
16
|
-
|
|
17
|
-
`enable` records bounded failure envelopes from the CLI and MCP gateway. Each
|
|
18
|
-
record contains a timestamp, random correlation ID, surface, operation, phase,
|
|
19
|
-
safe error name/code, a message fingerprint, and sanitized stack shape. It does
|
|
20
|
-
not retain the command arguments, query, error message, source, raw output,
|
|
21
|
-
environment, username, repository path, or source paths. Records are stored in
|
|
22
|
-
`.knodin/diagnostics/events.jsonl`, mode `0600`, capped at 500 events, and
|
|
23
|
-
pruned to the configured 1–365 day retention window.
|
|
24
|
-
|
|
25
|
-
`collect` runs local installation and deep graph diagnostics and combines their
|
|
26
|
-
redacted results with recent failure envelopes, metadata-only telemetry when
|
|
27
|
-
present, and at most 64 KiB/200 lines from the lifecycle indexer log. The
|
|
28
|
-
default collection window is 24 hours; `--since` accepts hours or days. Bundle
|
|
29
|
-
output must remain inside the repository and cannot traverse a symlink.
|
|
30
|
-
|
|
31
|
-
The bundle is gzip-compressed JSON, written mode `0600`, and includes a privacy
|
|
32
|
-
manifest, explicit omissions, and a redaction count. `inspect` validates the
|
|
33
|
-
manifest and a 10 MiB compressed-size ceiling before returning its contents.
|
|
34
|
-
Users should inspect the bundle before attaching it to Jira, GitHub, email, or
|
|
35
|
-
another support channel. knodin does not transmit it.
|
|
36
|
-
|
|
37
|
-
`clear` removes only the event journal. It does not delete previously created
|
|
38
|
-
bundles or ROI telemetry. `disable` stops future failure recording but preserves
|
|
39
|
-
existing events so disabling never silently destroys troubleshooting evidence.
|
|
40
|
-
Run `clear` when deletion is intended.
|
|
41
|
-
|
|
42
|
-
Diagnostics are evidence, not runtime-causality proof. Message fingerprints can
|
|
43
|
-
group identical sanitized failures but cannot reconstruct the original message.
|
|
44
|
-
Redaction is defense in depth; users remain responsible for inspecting an
|
|
45
|
-
artifact before sharing it outside their organization.
|
|
@@ -1,39 +0,0 @@
|
|
|
1
|
-
# Bounded Git-history review signals
|
|
2
|
-
|
|
3
|
-
`knodin review` and the MCP gateway's `review` operation return `signals` beside
|
|
4
|
-
the compatibility `riskScore`. The signal groups stay separate:
|
|
5
|
-
|
|
6
|
-
- `graphImpact`: changed files and affected flows from indexed relationships;
|
|
7
|
-
- `testGaps`: missing test-path/reference facts;
|
|
8
|
-
- `structuralCentrality`: inbound, outbound, and total indexed references for
|
|
9
|
-
each changed symbol;
|
|
10
|
-
- `history`: rename-aware churn, co-change commit facts, and coupling ratios.
|
|
11
|
-
|
|
12
|
-
History output includes the exact contributing commit IDs, availability,
|
|
13
|
-
confidence, configured bounds, commands executed, analyzed counts, truncation,
|
|
14
|
-
and explicit omissions. Results are sorted deterministically. CLI and MCP output
|
|
15
|
-
also retain the review's graph `staleness` field.
|
|
16
|
-
|
|
17
|
-
## Bounds and unavailable states
|
|
18
|
-
|
|
19
|
-
The defaults are 250 commits, 25 changed files, 100 related-file facts, a
|
|
20
|
-
2-second timeout per Git command, and an 8 MiB output ceiling. Review uses one
|
|
21
|
-
bounded `git log` pass after HEAD and shallow-state probes, and caches facts by
|
|
22
|
-
repository, HEAD, requested files, and bounds. User-provided file paths must be
|
|
23
|
-
repository-relative; revisions never enter the history command.
|
|
24
|
-
|
|
25
|
-
`shallow`, `unborn`, `git-unavailable`, `not-a-repository`, `timeout`, and
|
|
26
|
-
`unavailable` are explicit. Missing history never produces synthetic commits or
|
|
27
|
-
coupling. Shallow results contain only locally present commits and disclose that
|
|
28
|
-
omission. Commit, file, and related-file truncation likewise names what was
|
|
29
|
-
omitted and why.
|
|
30
|
-
|
|
31
|
-
## Interpretation limit
|
|
32
|
-
|
|
33
|
-
Co-change and coupling show correlation in the inspected history only. They do
|
|
34
|
-
not assert causation, defect likelihood, ownership, or mandatory remediation.
|
|
35
|
-
Treat them as review context alongside graph impact, test gaps, and structural
|
|
36
|
-
centrality—not as an opaque score or automated decision.
|
|
37
|
-
|
|
38
|
-
The reproducible synthetic-DAG evidence, raw replay, resource measurement, and
|
|
39
|
-
verifier are under `benchmarks/evaluations/c85-git-history/`.
|
|
@@ -1,37 +0,0 @@
|
|
|
1
|
-
# Progressive source evidence
|
|
2
|
-
|
|
3
|
-
`knodin evidence` returns exact local source through four deterministic levels:
|
|
4
|
-
|
|
5
|
-
1. `locate` — stable file identity, path, hash, size, freshness, and confidence.
|
|
6
|
-
2. `outline` — top-level declaration signatures and exact line ranges.
|
|
7
|
-
3. `evidence` — requested verbatim source lines with edit anchors intact.
|
|
8
|
-
4. `expand` — the complete verbatim file within caller-selected hard budgets.
|
|
9
|
-
|
|
10
|
-
CLI:
|
|
11
|
-
|
|
12
|
-
```bash
|
|
13
|
-
knodin evidence locate src/widget.ts
|
|
14
|
-
knodin evidence evidence src/widget.ts --start 10 --end 30
|
|
15
|
-
knodin evidence evidence src/widget.ts --continuation <evidence-handle> \
|
|
16
|
-
--baseline-hash <sha256> --baseline-bytes <bytes>
|
|
17
|
-
```
|
|
18
|
-
|
|
19
|
-
The single MCP gateway uses `operation: "evidence"`, `file`, and
|
|
20
|
-
`evidenceLevel`. Supply a returned `continuation` to recover omitted source.
|
|
21
|
-
Every response names returned and omitted material, a reason, whether more
|
|
22
|
-
exists, a stable continuation and evidence handle, freshness, confidence, the
|
|
23
|
-
path taken, and exact item/UTF-8-byte/local-token-estimate budgets.
|
|
24
|
-
The mandatory recoverability envelope requires at least 1,600 effective bytes
|
|
25
|
-
(the lower of the byte cap and four times the local token-estimate cap); smaller
|
|
26
|
-
requests fail closed instead of dropping contract fields.
|
|
27
|
-
|
|
28
|
-
Source omission occurs only when a server-issued evidence handle is valid and
|
|
29
|
-
both `baselineHash` and `baselineBytes` equal the current complete local file.
|
|
30
|
-
Missing or partial metadata/handle, a stale/tampered hash, source changed since
|
|
31
|
-
a handle was issued, or a renamed path selects a named `full-*` recovery path.
|
|
32
|
-
Invalid or tampered handles fail closed. Inputs
|
|
33
|
-
must identify an exact repository-relative regular file; ambiguous selection,
|
|
34
|
-
absolute/traversal paths, and symlinks are refused.
|
|
35
|
-
|
|
36
|
-
This protocol does not summarize source and does not support token-savings or
|
|
37
|
-
task-success claims. See the C83 checked-in replay and limitations for bounds.
|
|
@@ -1,91 +0,0 @@
|
|
|
1
|
-
# P&T access through Docusign GitHub Enterprise Server
|
|
2
|
-
|
|
3
|
-
The knodin GHES path is planned as
|
|
4
|
-
`github.docusignhq.com/Enterprise-Apps/knodin`, but this repository rename does
|
|
5
|
-
not create or verify that external repository. Until the mirror is renamed and
|
|
6
|
-
its knodin artifact is certified, P&T engineers must use the existing legacy
|
|
7
|
-
mirror and its documented release asset. `Enterprise-Apps` is a legacy
|
|
8
|
-
namespace; product changes must continue to originate in the authoritative
|
|
9
|
-
GitHub SaaS repository, not the downstream mirror.
|
|
10
|
-
|
|
11
|
-
The legacy `v0.3.0` release and its original artifact digest were verified on
|
|
12
|
-
2026-08-01. That evidence does not certify a renamed knodin artifact or path.
|
|
13
|
-
|
|
14
|
-
## Quick install without GitHub SaaS
|
|
15
|
-
|
|
16
|
-
Node.js 24 or newer and an authenticated GHES CLI are required. Download the
|
|
17
|
-
versioned release asset from GHES, verify its observed SHA-256, install it
|
|
18
|
-
without dependency lifecycle scripts, and initialize the existing checkout:
|
|
19
|
-
|
|
20
|
-
```bash
|
|
21
|
-
mkdir knodin-install && cd knodin-install
|
|
22
|
-
GH_HOST=github.docusignhq.com gh release download v0.3.0 \
|
|
23
|
-
--repo Enterprise-Apps/reckon-graph \
|
|
24
|
-
--pattern 'reckon-graph-0.3.0.tgz'
|
|
25
|
-
printf '%s %s\n' \
|
|
26
|
-
373ca7540e745915f195e51f4ed6383374e4df4a2b8663872b2f279380e60989 \
|
|
27
|
-
reckon-graph-0.3.0.tgz | shasum -a 256 --check
|
|
28
|
-
npm install --global --ignore-scripts ./reckon-graph-0.3.0.tgz
|
|
29
|
-
cd /path/to/existing/repository
|
|
30
|
-
reckon init
|
|
31
|
-
reckon doctor
|
|
32
|
-
reckon status --deep
|
|
33
|
-
```
|
|
34
|
-
|
|
35
|
-
Unverified Windows guidance: PowerShell provides
|
|
36
|
-
`Get-FileHash -Algorithm SHA256` instead of `shasum`, and the npm/CLI commands
|
|
37
|
-
are intended to be portable. A native Windows named-client run has not yet been
|
|
38
|
-
certified, so this is not a Windows support claim. The release page records the
|
|
39
|
-
versioned filename and observed digest; do not substitute a branch archive or a
|
|
40
|
-
mutable `latest` URL. GHES does not currently enforce immutable releases, which
|
|
41
|
-
is why digest verification and the separate signed-update trust remain
|
|
42
|
-
required.
|
|
43
|
-
|
|
44
|
-
The same 0.3.0 tarball was published to Docusign Artifactory at
|
|
45
|
-
`dts-productivity-engineering-npm-dev-local/knodin-0.3.0.tgz`. That raw
|
|
46
|
-
artifact path requires normal corporate Artifactory authentication and is not
|
|
47
|
-
an npm registry endpoint. GHES release download is the supported P&T bootstrap
|
|
48
|
-
until an approved internal npm virtual repository exposes the package metadata.
|
|
49
|
-
|
|
50
|
-
## Mirror operating model
|
|
51
|
-
|
|
52
|
-
1. Source commits and reviewed release tags originate in the GitHub SaaS
|
|
53
|
-
repository.
|
|
54
|
-
2. The GHES `main` branch and release tag are synchronized without rewriting
|
|
55
|
-
history.
|
|
56
|
-
3. The exact retained release tarball is attached to the matching GHES release;
|
|
57
|
-
it is not rebuilt on GHES.
|
|
58
|
-
4. A release is not certified merely because it appears on GHES. The final
|
|
59
|
-
cross-channel attestation must prove that npm, Artifactory, GitHub SaaS,
|
|
60
|
-
Homebrew, and GHES represent the same source and bytes.
|
|
61
|
-
5. Developers do not dual-push feature branches. Mirror synchronization is a
|
|
62
|
-
release/operations responsibility and must eventually use a narrowly scoped
|
|
63
|
-
workload identity rather than a personal SSH key.
|
|
64
|
-
|
|
65
|
-
The destination must eventually enforce repository visibility, branch
|
|
66
|
-
protection, required reviews/checks, security scans, commit/tag provenance, and
|
|
67
|
-
license notices. Today the repository is internal, but `main` is unprotected and
|
|
68
|
-
maintainers retain push access. Those are open governance controls, not
|
|
69
|
-
completed evidence. Future mirror automation must fail closed and must not
|
|
70
|
-
accept changes back into the authoritative repository.
|
|
71
|
-
|
|
72
|
-
## Required governance follow-up
|
|
73
|
-
|
|
74
|
-
Record or complete:
|
|
75
|
-
|
|
76
|
-
- repository-owner approval to disclose the source to P&T;
|
|
77
|
-
- enterprise GitHub/security approval for cross-organization private forks or
|
|
78
|
-
mirrors;
|
|
79
|
-
- legal/compliance confirmation for source and license visibility;
|
|
80
|
-
- the exact destination GitHub organization and repository name;
|
|
81
|
-
- confirmation of destination visibility and membership controls;
|
|
82
|
-
- the required branch-protection, review, scan, and retention policy;
|
|
83
|
-
- approval of a narrowly scoped GitHub App/workload identity, its installation
|
|
84
|
-
owners, secret store, rotation owner, and audit-log owner;
|
|
85
|
-
- an owner for incident response and mirror decommissioning.
|
|
86
|
-
|
|
87
|
-
The destination repository and current release asset are live. Branch
|
|
88
|
-
protection, enforced read-only direction, automated mirror synchronization,
|
|
89
|
-
production threshold-update trust, timed onboarding evidence, and a Windows
|
|
90
|
-
named-client release certification remain open gates; the existence of this
|
|
91
|
-
access path is not evidence that those separate controls are complete.
|