Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
37 commits
Select commit Hold shift + click to select a range
8f2582b
test(release): prove packaged desktop startup
yaacovcorcos Jul 24, 2026
2442dd7
Merge branch 'main' into agent/packaged-startup-proof
yaacovcorcos Jul 25, 2026
6a60c42
Merge remote-tracking branch 'origin/main' into agent/packaged-startu…
yaacovcorcos Jul 25, 2026
c7c51bc
Close packaged startup proof gaps
yaacovcorcos Jul 25, 2026
8bed41b
Preserve packaged smoke isolation
yaacovcorcos Jul 25, 2026
fc5f257
Avoid duplicate Windows tree kills
yaacovcorcos Jul 25, 2026
599abe7
Harden packaged smoke process cleanup
yaacovcorcos Jul 25, 2026
01a6e32
Harden packaged startup proof acceptance
yaacovcorcos Jul 25, 2026
756230c
Close packaged startup certification gaps
yaacovcorcos Jul 25, 2026
e29e9c8
Close exact packaged startup proof gaps
yaacovcorcos Jul 25, 2026
e69eb01
Bound packaged smoke process cleanup
yaacovcorcos Jul 25, 2026
d1ea51e
Harden packaged startup release proof
yaacovcorcos Jul 25, 2026
0248627
Close packaged startup review findings
yaacovcorcos Jul 25, 2026
4e3b034
Keep startup proof test lint-clean
yaacovcorcos Jul 25, 2026
c100697
Merge remote-tracking branch 'origin/main' into agent/packaged-startu…
yaacovcorcos Jul 25, 2026
5d71978
fix(release): close packaged startup proof gaps
yaacovcorcos Jul 25, 2026
412c094
fix(release): harden packaged startup cleanup
yaacovcorcos Jul 26, 2026
537c46d
fix(release): authenticate startup ownership records
yaacovcorcos Jul 26, 2026
9e14465
fix(release): bind cleanup to process instances
yaacovcorcos Jul 26, 2026
b3bfae9
fix(release): type cleanup authorities explicitly
yaacovcorcos Jul 26, 2026
2cca9ae
fix(release): close final startup proof findings
yaacovcorcos Jul 26, 2026
280a16c
fix(release): contain packaged startup by OS identity
yaacovcorcos Jul 26, 2026
1f7623b
Merge remote-tracking branch 'origin/main' into agent/packaged-startu…
yaacovcorcos Jul 26, 2026
254cc2e
fix(release): fail closed after sentinel loss
yaacovcorcos Jul 26, 2026
82b7617
fix(release): close packaged cleanup containment gaps
yaacovcorcos Jul 26, 2026
a388f0d
Merge remote-tracking branch 'origin/main' into agent/packaged-startu…
yaacovcorcos Jul 26, 2026
4f1c32e
fix(release): fail closed on startup cleanup uncertainty
yaacovcorcos Jul 26, 2026
0565243
fix(release): authenticate Windows startup verifier
yaacovcorcos Jul 26, 2026
6f0591c
ci(release): exercise Windows startup launcher
yaacovcorcos Jul 26, 2026
e7b476d
Merge remote-tracking branch 'origin/main' into agent/packaged-startu…
yaacovcorcos Jul 26, 2026
7b9ea4e
fix(release): close packaged startup proof gaps
yaacovcorcos Jul 26, 2026
a4d3faa
fix(release): harden packaged startup certification
yaacovcorcos Jul 26, 2026
4f792fa
Merge remote-tracking branch 'origin/main' into agent/packaged-startu…
yaacovcorcos Jul 26, 2026
29de13a
Fix packaged startup reconnect hydration
yaacovcorcos Jul 26, 2026
fd361bc
fix(release): bind packaged startup containment lifetime
yaacovcorcos Jul 26, 2026
51a07d7
fix(release): request sentinel cleanup over IPC
yaacovcorcos Jul 26, 2026
9a0d140
fix(release): bind Windows verifier authority to pipe
yaacovcorcos Jul 26, 2026
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
3 changes: 3 additions & 0 deletions .github/workflows/ci.yml
Original file line number Diff line number Diff line change
Expand Up @@ -205,6 +205,9 @@ jobs:
bun run --cwd apps/desktop test src/desktopBackendSupervisor.test.ts src/backendProcessTree.test.ts
bun run --cwd apps/server test src/desktopParentShutdown.test.ts

- name: Test packaged startup Windows launcher
run: bun x vitest run scripts/verify-packaged-desktop-startup.test.ts

- name: Exercise Windows release staging
run: bun run release:smoke

Expand Down
37 changes: 35 additions & 2 deletions .github/workflows/release.yml
Original file line number Diff line number Diff line change
Expand Up @@ -22,8 +22,7 @@ on:
type: boolean

permissions:
contents: write
id-token: write
contents: read

concurrency:
group: scient-desktop-release
Expand Down Expand Up @@ -208,6 +207,8 @@ jobs:

build:
name: Build ${{ matrix.label }}
permissions:
contents: read
needs: preflight
runs-on: ${{ matrix.runner }}
timeout-minutes: ${{ matrix.timeout_minutes }}
Expand Down Expand Up @@ -245,6 +246,7 @@ jobs:
with:
ref: ${{ needs.preflight.outputs.ref }}
fetch-depth: 0
persist-credentials: false

- name: Setup Bun
uses: oven-sh/setup-bun@0c5077e51419868618aeaa5fe8019c62421857d6 # v2
Expand Down Expand Up @@ -309,6 +311,7 @@ jobs:
"${{ needs.preflight.outputs.version }}"

- name: Build Windows desktop artifact
id: build_windows
if: matrix.platform == 'win'
shell: bash
env:
Expand Down Expand Up @@ -386,6 +389,31 @@ jobs:
fi
fi

- name: Smoke exact packaged desktop startup
if: ${{ matrix.platform != 'linux' }}
shell: bash
env:
SCIENT_PACKAGED_STARTUP_DIAGNOSTICS_DIR: ${{ github.workspace }}/packaged-startup-diagnostics
SCIENT_WINDOWS_PUBLISHER_SUBJECT: ${{ vars.SCIENT_WINDOWS_PUBLISHER_SUBJECT }}
run: |
node scripts/verify-packaged-desktop-startup.ts \
--assets-dir release-publish \
--platform "${{ matrix.platform }}" \
--arch "${{ matrix.arch }}" \
--version "${{ needs.preflight.outputs.version }}" \
--commit "${{ github.sha }}" \
--allow-unsigned-windows "${{ steps.build_windows.outputs.windows_signed != 'true' }}" \
--windows-publisher-subject "$SCIENT_WINDOWS_PUBLISHER_SUBJECT"

- name: Upload packaged startup failure diagnostics
if: ${{ failure() && matrix.platform != 'linux' }}
uses: actions/upload-artifact@043fb46d1a93c77aae656e7c1c64a875d1fc6a0a # v7
with:
name: packaged-startup-diagnostics-${{ matrix.platform }}-${{ matrix.arch }}
path: packaged-startup-diagnostics/*
if-no-files-found: warn
retention-days: 7

- name: Upload build artifacts
uses: actions/upload-artifact@043fb46d1a93c77aae656e7c1c64a875d1fc6a0a # v7
with:
Expand All @@ -395,6 +423,9 @@ jobs:

publish_cli:
name: Publish CLI to npm
permissions:
contents: read
id-token: write
if: ${{ needs.preflight.outputs.publish_release == 'true' && vars.SCIENT_PUBLISH_CLI == '1' }}
needs: [preflight, build]
runs-on: ubuntu-24.04
Expand Down Expand Up @@ -432,6 +463,8 @@ jobs:

release:
name: Publish GitHub Release
permissions:
contents: write
if: ${{ needs.preflight.outputs.publish_release == 'true' }}
needs: [preflight, build]
runs-on: ubuntu-24.04
Expand Down
26 changes: 26 additions & 0 deletions apps/desktop/src/backendProcessTree.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -21,6 +21,10 @@ describe("forceTerminateBackendProcessTree", () => {
detached: false,
stdio: ["ignore", "inherit", "inherit", "ipc"],
});
expect(backendProcessContainmentOptions(false, "darwin", false)).toEqual({
detached: false,
stdio: ["ignore", "inherit", "inherit", "ipc"],
});
});

it("kills the detached POSIX process group", async () => {
Expand Down Expand Up @@ -74,6 +78,28 @@ describe("forceTerminateBackendProcessTree", () => {
);
});

it.each(["darwin", "win32"] as const)(
"never signals a backend PID when external containment owns cleanup on %s",
async (platform) => {
const killProcessGroup = vi.fn();
const spawnProcess = vi.fn();

await expect(
forceTerminateBackendProcessTree(
{ pid: 4321 },
{
platform,
retainedByExternalContainment: true,
killProcessGroup,
spawnProcess: spawnProcess as never,
},
),
).rejects.toThrow("External packaged-startup containment retains cleanup authority");
expect(killProcessGroup).not.toHaveBeenCalled();
expect(spawnProcess).not.toHaveBeenCalled();
},
);

it("does not treat a missing Windows root as successful descendant cleanup", async () => {
const process = new EventEmitter();
const spawnProcess = vi.fn(() => process);
Expand Down
9 changes: 8 additions & 1 deletion apps/desktop/src/backendProcessTree.ts
Original file line number Diff line number Diff line change
Expand Up @@ -7,16 +7,18 @@ import { resolveWindowsSystemRoot } from "@synara/shared/windowsProcess";
export interface ForceTerminateBackendProcessTreeOptions {
readonly platform?: NodeJS.Platform;
readonly env?: NodeJS.ProcessEnv;
readonly retainedByExternalContainment?: boolean;
readonly killProcessGroup?: (pid: number, signal: NodeJS.Signals) => void;
readonly spawnProcess?: typeof spawn;
}

export function backendProcessContainmentOptions(
captureLogs: boolean,
platform: NodeJS.Platform = process.platform,
isolatePosixProcessGroup = true,
): Pick<SpawnOptions, "detached" | "stdio"> {
return {
detached: platform !== "win32",
detached: platform !== "win32" && isolatePosixProcessGroup,
stdio: captureLogs
? ["ignore", "pipe", "pipe", "ipc"]
: ["ignore", "inherit", "inherit", "ipc"],
Expand Down Expand Up @@ -57,6 +59,11 @@ export async function forceTerminateBackendProcessTree(
child: Pick<ChildProcess, "pid">,
options: ForceTerminateBackendProcessTreeOptions = {},
): Promise<void> {
if (options.retainedByExternalContainment) {
throw new Error(
"External packaged-startup containment retains cleanup authority; refusing backend PID-tree signaling.",
);
}
const pid = child.pid;
if (!pid || pid <= 0) return;

Expand Down
Loading
Loading