@junghanacs/entwurf 0.45.0 → 0.50.0
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/AGENTS.md +3 -2
- package/BASELINE.md +110 -1
- package/CHANGELOG.md +169 -0
- package/CONTRIBUTING.md +1 -1
- package/ENTWURF-CLI.json +1229 -0
- package/ENTWURF.json +63 -15
- package/FAQ.md +4 -4
- package/README.md +124 -188
- package/VERIFY.md +2 -2
- package/docs/external-mcp-host.md +3 -3
- package/docs/setup-clean-host.md +37 -12
- package/mcp/entwurf-bridge/dist/ENTWURF.json +63 -15
- package/mcp/entwurf-bridge/dist/mcp/entwurf-bridge/src/index.js +19 -8
- package/mcp/entwurf-bridge/dist/pi-extensions/lib/entwurf-v2-visible-resume.js +483 -18
- package/mcp/entwurf-bridge/dist/pi-extensions/lib/fresh-call-dispatch.js +47 -10
- package/mcp/entwurf-bridge/dist/pi-extensions/lib/herdr-fresh-call.js +613 -33
- package/mcp/entwurf-bridge/dist/pi-extensions/lib/herdr-resume-call.js +497 -0
- package/mcp/entwurf-bridge/dist/pi-extensions/lib/meta-session.js +3 -1
- package/mcp/entwurf-bridge/dist/pi-extensions/lib/mux-resume-call.js +50 -5
- package/mcp/entwurf-bridge/dist/pi-extensions/lib/pi-durable-fresh-preflight.js +7 -1
- package/mcp/entwurf-bridge/dist/pi-extensions/lib/pi-durable-location.js +347 -0
- package/mcp/entwurf-bridge/dist/pi-extensions/lib/pi-durable-owner-fence.js +1132 -0
- package/mcp/entwurf-bridge/dist/pi-extensions/lib/pi-durable-runtime.js +1 -0
- package/mcp/entwurf-bridge/dist/pi-extensions/lib/resume-launch-identity.js +18 -11
- package/mcp/entwurf-bridge/dist/pi-extensions/meta-bridge-pi-durable.js +30 -15
- package/mcp/entwurf-bridge/dist/scripts/herdr-rails.mjs +40 -7
- package/mcp/entwurf-bridge/dist/scripts/meta-bridge-fresh-cut.js +1 -27
- package/mcp/entwurf-bridge/dist/scripts/meta-bridge-prune.js +1 -1
- package/mcp/entwurf-bridge/dist/scripts/meta-facts.js +1 -1
- package/mcp/entwurf-bridge/dist/scripts/peer-facts.js +1 -1
- package/mcp/entwurf-bridge/src/index.ts +28 -8
- package/mcp/entwurf-bridge/tsconfig.build.json +7 -1
- package/native/win32-x64/manifest.json +1 -1
- package/package.json +6 -3
- package/pi/pi-durable/bootstrap.mjs +179 -6
- package/pi/pi-durable/carrier/runtime.js +108 -9
- package/pi/pi-durable/carrier/session-name.js +11 -0
- package/pi/pi-durable/carrier/sessions.js +96 -20
- package/pi/pi-durable/overlay/upstream-pin.json +11 -5
- package/pi-extensions/lib/entwurf-v2-visible-resume.ts +710 -38
- package/pi-extensions/lib/fresh-call-dispatch.ts +64 -12
- package/pi-extensions/lib/herdr-fresh-call.ts +919 -40
- package/pi-extensions/lib/herdr-resume-call.ts +810 -0
- package/pi-extensions/lib/meta-session.ts +13 -8
- package/pi-extensions/lib/mux-resume-call.ts +77 -4
- package/pi-extensions/lib/pi-durable-fresh-preflight.ts +9 -2
- package/pi-extensions/lib/pi-durable-location.ts +446 -0
- package/pi-extensions/lib/pi-durable-owner-fence.ts +1461 -0
- package/pi-extensions/lib/pi-durable-runtime.ts +4 -0
- package/pi-extensions/lib/resume-launch-identity.ts +31 -12
- package/pi-extensions/meta-bridge-pi-durable.ts +29 -15
- package/run.sh +411 -657
- package/scripts/check-claude-launch.ts +341 -0
- package/scripts/check-entwurf-v2-surface.ts +2 -2
- package/scripts/check-fresh-call-dispatch.ts +70 -9
- package/scripts/check-fresh-cut-gate.sh +23 -9
- package/scripts/check-gate-qualification.ts +18 -13
- package/scripts/check-herdr-activation.ts +301 -23
- package/scripts/check-herdr-fresh-call.ts +161 -25
- package/scripts/check-herdr-plugin-build.ts +173 -1
- package/scripts/check-herdr-plugin.ts +28 -2
- package/scripts/check-herdr-runtime-bootstrap.ts +136 -0
- package/scripts/check-install-surface.ts +1236 -6
- package/scripts/check-meta-doctor-oracle.sh +1 -1
- package/scripts/check-meta-facts.ts +20 -1
- package/scripts/check-mux-resume-call.ts +28 -0
- package/scripts/check-peer-facts.ts +39 -2
- package/scripts/cli-help.mjs +222 -0
- package/scripts/dev-bin.sh +39 -2
- package/scripts/herdr-activation.mjs +28 -6
- package/scripts/herdr-operator-link.mjs +389 -0
- package/scripts/herdr-plugin-activate.mjs +5 -0
- package/scripts/herdr-plugin-deactivate.mjs +18 -5
- package/scripts/herdr-rails.mjs +43 -7
- package/scripts/herdr-runtime.mjs +27 -0
- package/scripts/meta-bridge-fresh-cut.ts +1 -27
- package/scripts/meta-bridge-prune.ts +1 -1
- package/scripts/meta-facts.ts +1 -1
- package/scripts/mutants/agent-guidance.json +4 -4
- package/scripts/mutants/claude-launch.json +142 -0
- package/scripts/mutants/cli-help-source.json +420 -0
- package/scripts/mutants/dev-gate-dispatch.json +109 -0
- package/scripts/mutants/fresh-call-dispatch.json +38 -2
- package/scripts/mutants/fresh-cut.json +15 -0
- package/scripts/mutants/herdr-activation.json +128 -0
- package/scripts/mutants/herdr-fresh-call.json +278 -5
- package/scripts/mutants/herdr-operator-link.json +318 -0
- package/scripts/mutants/herdr-plugin-build.json +55 -0
- package/scripts/mutants/herdr-plugin.json +17 -0
- package/scripts/mutants/herdr-resume-call.json +288 -0
- package/scripts/mutants/herdr-runtime-bootstrap.json +48 -0
- package/scripts/mutants/meta-facts.json +15 -0
- package/scripts/mutants/mux-resume-call.json +63 -2
- package/scripts/mutants/peer-facts.json +15 -0
- package/scripts/mutants/pi-durable.json +979 -1
- package/scripts/mutants/source-install.json +35 -0
- package/scripts/mutants/v2-surface.json +2 -2
- package/scripts/mutants/v2-visible-resume.json +255 -6
- package/scripts/peer-facts.ts +1 -1
- package/scripts/smoke-agy-install-state.sh +66 -0
- package/scripts/smoke-meta-prune.sh +23 -0
- package/scripts/smoke-omp-fresh-live.ts +12 -3
- package/scripts/smoke-omp-receive-live.ts +7 -1
- package/scripts/smoke-pi-durable-fresh-live.ts +12 -2
- package/team.json.example +39 -0
|
@@ -30,6 +30,7 @@ import os from "node:os";
|
|
|
30
30
|
import path from "node:path";
|
|
31
31
|
import { pathToFileURL } from "node:url";
|
|
32
32
|
import { reclaimOnExit } from "./lib/reclaim-on-exit.ts";
|
|
33
|
+
import { treeDigest as contentDigest } from "./lib/tree-digest.ts";
|
|
33
34
|
|
|
34
35
|
let passed = 0;
|
|
35
36
|
function ok(label: string, cond: boolean): void {
|
|
@@ -159,6 +160,7 @@ interface Leaf {
|
|
|
159
160
|
checkoutRoot?: string;
|
|
160
161
|
acquire?: unknown;
|
|
161
162
|
resolveCommit?: (root: string) => string;
|
|
163
|
+
admitCandidate?: unknown;
|
|
162
164
|
}) => BootstrapResult;
|
|
163
165
|
removeOwnedRuntime: (args: { env: NodeJS.ProcessEnv }) => { removed: string[]; reason: string | null };
|
|
164
166
|
}
|
|
@@ -1261,4 +1263,138 @@ function install(
|
|
|
1261
1263
|
);
|
|
1262
1264
|
}
|
|
1263
1265
|
|
|
1266
|
+
// ── 29. a candidate may be ADMITTED by its caller, synchronously, before the swap (#153) ─
|
|
1267
|
+
// Byte claims in this section use the shared content + mode + topology digest; this file's own
|
|
1268
|
+
// `treeDigest` (paths and sizes) cannot tell a same-length 0.21.0 → 0.22.0 swap from no swap.
|
|
1269
|
+
{
|
|
1270
|
+
/** Everything a refusal must have left, captured as VALUES immediately — before any later run. */
|
|
1271
|
+
const capture = (layout: Layout) => ({
|
|
1272
|
+
active: contentDigest(layout.activeDir),
|
|
1273
|
+
staging: fs.existsSync(layout.stagingDir),
|
|
1274
|
+
previous: fs.existsSync(layout.previousDir),
|
|
1275
|
+
cache: fs.existsSync(layout.cacheDir),
|
|
1276
|
+
journal: fs.readFileSync(layout.journalPath, "utf8"),
|
|
1277
|
+
});
|
|
1278
|
+
/** One bootstrap of LOCK_B over a finished LOCK_A runtime, with an acquire that also fills OUR cache. */
|
|
1279
|
+
const over = (tag: string, admitCandidate: unknown) => {
|
|
1280
|
+
const env = world(tag);
|
|
1281
|
+
const layout = resolveRuntimeLayout(env);
|
|
1282
|
+
install(env);
|
|
1283
|
+
const acquire = (args: AcquireArgs): AcquireResult => {
|
|
1284
|
+
fs.mkdirSync(args.cacheDir, { recursive: true });
|
|
1285
|
+
fs.writeFileSync(path.join(args.cacheDir, "fetched.tgz"), "fixture\n");
|
|
1286
|
+
return fixtureAcquire()(args);
|
|
1287
|
+
};
|
|
1288
|
+
const before = capture(layout);
|
|
1289
|
+
const code = refusal(() =>
|
|
1290
|
+
bootstrapRuntime({
|
|
1291
|
+
env,
|
|
1292
|
+
lock: LOCK_B,
|
|
1293
|
+
checkoutRoot: fixtureCheckout(LOCK_B),
|
|
1294
|
+
acquire,
|
|
1295
|
+
resolveCommit: () => COMMIT_A,
|
|
1296
|
+
admitCandidate,
|
|
1297
|
+
}),
|
|
1298
|
+
);
|
|
1299
|
+
const after = capture(layout);
|
|
1300
|
+
return { env, layout, before, after, code };
|
|
1301
|
+
};
|
|
1302
|
+
|
|
1303
|
+
// (a) the call: once, with the staged package root, while the active runtime is still the old one.
|
|
1304
|
+
const seen: { arg: string; stagedManifest: boolean; activeAtCall: string; previousAtCall: boolean }[] = [];
|
|
1305
|
+
const env = world("admit-order");
|
|
1306
|
+
const layoutA = resolveRuntimeLayout(env);
|
|
1307
|
+
install(env);
|
|
1308
|
+
const activeBefore = contentDigest(layoutA.activeDir);
|
|
1309
|
+
const result = bootstrapRuntime({
|
|
1310
|
+
env,
|
|
1311
|
+
lock: LOCK_B,
|
|
1312
|
+
checkoutRoot: fixtureCheckout(LOCK_B),
|
|
1313
|
+
acquire: fixtureAcquire(),
|
|
1314
|
+
resolveCommit: () => COMMIT_A,
|
|
1315
|
+
admitCandidate: (arg: string) => {
|
|
1316
|
+
seen.push({
|
|
1317
|
+
arg,
|
|
1318
|
+
stagedManifest: fs.existsSync(path.join(arg, "package.json")),
|
|
1319
|
+
activeAtCall: contentDigest(layoutA.activeDir),
|
|
1320
|
+
previousAtCall: fs.existsSync(layoutA.previousDir),
|
|
1321
|
+
});
|
|
1322
|
+
},
|
|
1323
|
+
});
|
|
1324
|
+
const activeAfter = contentDigest(layoutA.activeDir);
|
|
1325
|
+
const expectedArg = path.join(layoutA.stagingDir, "node_modules", PACKAGE);
|
|
1326
|
+
ok(
|
|
1327
|
+
"[QK:HRB-ADMISSION-BEFORE-SWAP] a caller's admission is asked exactly once, about the VERIFIED staged package " +
|
|
1328
|
+
"root, while the active runtime is still byte-for-byte the one it replaces and no backup exists yet — and an " +
|
|
1329
|
+
`admission that returns nothing lets the swap finish (calls=${seen.length} arg=${seen[0]?.arg === expectedArg} staged=${seen[0]?.stagedManifest} active-at-call-unchanged=${seen[0]?.activeAtCall === activeBefore} previous=${seen[0]?.previousAtCall} result=${result.phase}/${result.changed}/${result.journal.artifactIdentity.version} active-replaced=${activeAfter !== activeBefore})`,
|
|
1330
|
+
seen.length === 1 &&
|
|
1331
|
+
seen[0].arg === expectedArg &&
|
|
1332
|
+
seen[0].stagedManifest &&
|
|
1333
|
+
seen[0].activeAtCall === activeBefore &&
|
|
1334
|
+
!seen[0].previousAtCall &&
|
|
1335
|
+
result.phase === "runtime-ready" &&
|
|
1336
|
+
result.changed &&
|
|
1337
|
+
result.journal.artifactIdentity.version === LOCK_B.version &&
|
|
1338
|
+
activeAfter !== activeBefore,
|
|
1339
|
+
);
|
|
1340
|
+
|
|
1341
|
+
// (b) a refusal is reclaimed like a failed candidate. The state is judged from the values captured
|
|
1342
|
+
// right after the refusal; the retry is a SEPARATE run, judged on its own afterwards.
|
|
1343
|
+
class Refused extends Error {
|
|
1344
|
+
code = "fixture-candidate-refused";
|
|
1345
|
+
}
|
|
1346
|
+
const refused = over("admit-refused", () => {
|
|
1347
|
+
throw new Refused("no");
|
|
1348
|
+
});
|
|
1349
|
+
const torn = JSON.parse(refused.after.journal) as Journal;
|
|
1350
|
+
const refusalState =
|
|
1351
|
+
refused.code === "fixture-candidate-refused" &&
|
|
1352
|
+
refused.after.active === refused.before.active &&
|
|
1353
|
+
!refused.after.staging &&
|
|
1354
|
+
!refused.after.previous &&
|
|
1355
|
+
!refused.after.cache &&
|
|
1356
|
+
torn.phase === "installing" &&
|
|
1357
|
+
torn.artifactIdentity.version === LOCK_B.version &&
|
|
1358
|
+
torn.previousRuntime?.version === LOCK_A.version;
|
|
1359
|
+
const retry = refusal(() => install(refused.env, LOCK_B));
|
|
1360
|
+
const retried = readCertifiedJournal(refused.layout);
|
|
1361
|
+
const retryState =
|
|
1362
|
+
retry === null && retried?.phase === "runtime-ready" && retried.artifactIdentity.version === LOCK_B.version;
|
|
1363
|
+
ok(
|
|
1364
|
+
"[QK:HRB-ADMISSION-REFUSAL-RETAINS] a refused admission propagates the caller's own error and is reclaimed " +
|
|
1365
|
+
"exactly like a candidate that failed verification: the active runtime is byte-identical, staging, backup " +
|
|
1366
|
+
"and our cache are gone, and the journal is the `installing` retry authority still carrying the finished " +
|
|
1367
|
+
"runtime's provenance — not a zero-write rollback. Judged on values captured BEFORE any retry; the retry " +
|
|
1368
|
+
"without the refusal is a separate later run that goes green " +
|
|
1369
|
+
`(after-refusal: code=${refused.code} active-identical=${refused.after.active === refused.before.active} staging=${refused.after.staging} previous=${refused.after.previous} cache=${refused.after.cache} journal=${torn.phase}/${torn.artifactIdentity.version}/prev=${torn.previousRuntime?.version} | then-retry: ${retry ?? "ok"}/${retried?.phase}/${retried?.artifactIdentity.version})`,
|
|
1370
|
+
refusalState && retryState,
|
|
1371
|
+
);
|
|
1372
|
+
|
|
1373
|
+
// (c) admission is synchronous and says nothing: any returned value — a truthy one or a Promise — is refused.
|
|
1374
|
+
const truthy = over("admit-truthy", () => true);
|
|
1375
|
+
const promised = over("admit-promise", () => Promise.resolve());
|
|
1376
|
+
ok(
|
|
1377
|
+
"[QK:HRB-ADMISSION-VOID-ONLY] an admission that RETURNS anything is refused by name instead of being read as " +
|
|
1378
|
+
"consent — a Promise would otherwise let an asynchronous refusal arrive after the swap it was meant to stop " +
|
|
1379
|
+
`(truthy=${truthy.code}/kept=${truthy.after.active === truthy.before.active} promise=${promised.code}/kept=${promised.after.active === promised.before.active})`,
|
|
1380
|
+
truthy.code === "runtime-candidate-admission-invalid" &&
|
|
1381
|
+
truthy.after.active === truthy.before.active &&
|
|
1382
|
+
promised.code === "runtime-candidate-admission-invalid" &&
|
|
1383
|
+
promised.after.active === promised.before.active,
|
|
1384
|
+
);
|
|
1385
|
+
|
|
1386
|
+
// (d) something that cannot be called is refused before the first byte, journal included.
|
|
1387
|
+
const shaped = over("admit-shape", {});
|
|
1388
|
+
ok(
|
|
1389
|
+
"[QK:HRB-ADMISSION-CALLBACK-SHAPE] an admission that is not a function is refused by name before the first " +
|
|
1390
|
+
"byte — the journal is not even moved to `installing` — instead of failing as a TypeError half-way through " +
|
|
1391
|
+
`the transaction (code=${shaped.code} journal-unchanged=${shaped.after.journal === shaped.before.journal} active-unchanged=${shaped.after.active === shaped.before.active} staging=${shaped.after.staging} cache=${shaped.after.cache})`,
|
|
1392
|
+
shaped.code === "runtime-candidate-admission-invalid" &&
|
|
1393
|
+
shaped.after.journal === shaped.before.journal &&
|
|
1394
|
+
shaped.after.active === shaped.before.active &&
|
|
1395
|
+
!shaped.after.staging &&
|
|
1396
|
+
!shaped.after.cache,
|
|
1397
|
+
);
|
|
1398
|
+
}
|
|
1399
|
+
|
|
1264
1400
|
console.log(`\ncheck-herdr-runtime-bootstrap: ${passed} assertions passed`);
|