mirror of
https://github.com/nexu-io/open-design.git
synced 2026-10-27 09:51:12 +01:00
* feat(standalone): add shell-neutral exact lifecycle * feat(terminal): add native exact cold-start carrier * test(terminal): exercise mutable exact channel heads * feat(standalone): model synced blobs and Shell updates * feat(terminal): exercise the complete Shell lifecycle * feat(release): add the independent exact Terminal line * test(exact): run the full local Terminal release line * feat(terminal): harden exact Sidecar lifecycle * feat(standalone): prove exact update transactions * refactor(standalone): separate shell lifecycle dimensions * refactor(e2e): decouple terminal exact drivers * refactor(terminal): isolate exact sidecar fixture host * feat(terminal): gate handoff on physical retirement * refactor(release): gate exact activation on public acceptance * test(terminal): formalize guarded lifecycle algebra * fix(terminal): guard shell installer handoff * feat(terminal): add exact bootloader handoff * feat(terminal): integrate exact sidecar lifecycle * refactor(exact): rename validation channels * test(terminal): harden exact release acceptance * fix(postinstall): build standalone dependencies first Generated-By: looper 0.11.2 (runner=fixer, agent=codex) * fix(ci): build the native Terminal scene Generated-By: looper 0.11.2 (runner=fixer, agent=codex) * test(ci): cover exact convergence handoff routing Generated-By: looper 0.11.2 (runner=fixer, agent=codex) * fix(exact): isolate terminal validation from main CI * fix(terminal): complete shell update confirmation Generated-By: looper 0.11.2 (runner=fixer, agent=codex) * fix(exact): bind acceptance to installed manifest Generated-By: looper 0.11.2 (runner=fixer, agent=codex) * fix(standalone): block occupied shell installs Generated-By: looper 0.11.2 (runner=fixer, agent=codex) --------- Co-authored-by: Looper <looper@noreply.github.com>
99 lines
5.1 KiB
TypeScript
99 lines
5.1 KiB
TypeScript
import { describe, expect, it } from "vitest";
|
|
|
|
import {
|
|
initialSharedLifecycleState,
|
|
initialShellUpdaterSnapshot,
|
|
projectSharedLifecycleStatus,
|
|
reduceSharedLifecycleState,
|
|
reduceShellUpdaterSnapshot,
|
|
type SharedLifecycleState,
|
|
type StandaloneShellUpdaterSnapshot,
|
|
} from "../src/index.js";
|
|
|
|
const now = "2026-08-25T00:00:00.000Z";
|
|
const later = "2026-08-25T00:00:30.000Z";
|
|
const generation = "a".repeat(64);
|
|
const shell = { type: "terminal", version: "0.1.0", buildHash: "b".repeat(64), digest: "c".repeat(64) };
|
|
const attachment = { id: "terminal", shell };
|
|
const scope = { channel: "somechan", namespace: "shared" };
|
|
|
|
function running(): SharedLifecycleState {
|
|
return reduceSharedLifecycleState(initialSharedLifecycleState(scope), {
|
|
type: "start",
|
|
generationId: generation,
|
|
bindingDigest: "e".repeat(64),
|
|
instanceId: "instance-a",
|
|
attachment,
|
|
heartbeatAt: now,
|
|
leaseExpiresAt: later,
|
|
capability: { candidateHash: "d".repeat(64), presentedHash: null },
|
|
});
|
|
}
|
|
|
|
function handoff(): StandaloneShellUpdaterSnapshot["handoff"] {
|
|
return {
|
|
interaction: "restart-and-install",
|
|
releaseVersion: "0.2.0-somechan.1",
|
|
target: "darwin-arm64",
|
|
shell: { type: "terminal", version: "0.2.0", buildHash: "e".repeat(64) },
|
|
artifact: { path: "/tmp/candidate", sha256: "f".repeat(64), size: 1, mediaType: "application/gzip" },
|
|
};
|
|
}
|
|
|
|
describe("orthogonal Standalone lifecycle models", () => {
|
|
it("keeps attachments inside one instance aggregate without changing generation health", () => {
|
|
let state = running();
|
|
state = reduceSharedLifecycleState(state, {
|
|
type: "start",
|
|
generationId: generation,
|
|
bindingDigest: "e".repeat(64),
|
|
instanceId: "ignored-existing-instance",
|
|
attachment: { id: "electron", shell: { type: "electron", version: "1.0.0", buildHash: "1".repeat(64), digest: "2".repeat(64) } },
|
|
heartbeatAt: now,
|
|
leaseExpiresAt: later,
|
|
});
|
|
expect(projectSharedLifecycleStatus(state, 5_000)).toMatchObject({ generationId: generation, instanceId: "instance-a", references: 2 });
|
|
});
|
|
|
|
it("makes force-stop sealing irreversible except through exact completion or expiry", () => {
|
|
let state = running();
|
|
state = reduceSharedLifecycleState(state, {
|
|
type: "reserve-transition",
|
|
transition: { token: "transition-a", kind: "content-restart", phase: "reserved", fence: state.fence, acquiredAt: now, expiresAt: later },
|
|
});
|
|
const reservedFence = state.fence;
|
|
state = reduceSharedLifecycleState(state, { type: "force-stop", token: "transition-a", fence: reservedFence, requestedAt: now, expiresAt: later });
|
|
expect(state).toMatchObject({ state: "stopped", transition: { phase: "stopped-sealed", fence: reservedFence + 1 } });
|
|
expect(() => reduceSharedLifecycleState(state, { type: "release-transition", token: "transition-a", fence: state.fence }))
|
|
.toThrow("cannot be released");
|
|
state = reduceSharedLifecycleState(state, {
|
|
type: "complete-start",
|
|
token: "transition-a",
|
|
fence: state.fence,
|
|
generationId: generation,
|
|
bindingDigest: "e".repeat(64),
|
|
instanceId: "instance-b",
|
|
attachment,
|
|
heartbeatAt: now,
|
|
leaseExpiresAt: later,
|
|
});
|
|
expect(state).toMatchObject({ state: "running", generationId: generation, instanceId: "instance-b", transition: null });
|
|
});
|
|
|
|
it("derives Shell updater actions and binds every install phase to candidate and attempt identities", () => {
|
|
let state = initialShellUpdaterSnapshot("terminal");
|
|
state = reduceShellUpdaterSnapshot(state, { expectedRevision: state.revision, state: "checking" });
|
|
state = reduceShellUpdaterSnapshot(state, { expectedRevision: state.revision, state: "available", candidateId: "candidate-a" });
|
|
expect(state.actions).toEqual([{ id: "download", emphasis: "primary" }]);
|
|
expect(() => reduceShellUpdaterSnapshot(state, { expectedRevision: state.revision, state: "downloading", candidateId: "candidate-b" }))
|
|
.toThrow("candidate changed concurrently");
|
|
state = reduceShellUpdaterSnapshot(state, { expectedRevision: state.revision, state: "downloading", candidateId: "candidate-a", progress: { completed: 0, total: 1 } });
|
|
state = reduceShellUpdaterSnapshot(state, { expectedRevision: state.revision, state: "ready", candidateId: "candidate-a", progress: { completed: 1, total: 1 }, handoff: handoff() });
|
|
state = reduceShellUpdaterSnapshot(state, { expectedRevision: state.revision, state: "applying", candidateId: "candidate-a", installAttemptId: "install-a", handoff: handoff() });
|
|
state = reduceShellUpdaterSnapshot(state, { expectedRevision: state.revision, state: "handed-off", candidateId: "candidate-a", installAttemptId: "install-a", handoff: handoff() });
|
|
expect(() => reduceShellUpdaterSnapshot(state, { expectedRevision: state.revision, state: "checking" })).toThrow("invalid Shell updater transition");
|
|
state = reduceShellUpdaterSnapshot(state, { expectedRevision: state.revision, state: "installed", candidateId: "candidate-a", installAttemptId: "install-a", handoff: handoff() });
|
|
expect(state).toMatchObject({ state: "installed", candidateId: "candidate-a", installAttemptId: "install-a", actions: [] });
|
|
});
|
|
});
|