Skip to content

Commit 1478a37

Browse files
committed
test: add feature-final stack verification
1 parent 0fc3865 commit 1478a37

14 files changed

Lines changed: 592 additions & 4 deletions

File tree

.github/workflows/release.yml

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -66,6 +66,9 @@ jobs:
6666
path: dist/release
6767
- name: Restore executable artifact modes
6868
run: chmod 755 dist/release/portreeve-v*
69+
- if: startsWith(matrix.platform, 'linux-')
70+
name: Verify a real mixed process and Docker stack
71+
run: bun run stacks:verify
6972
- run: bun run release:verify -- --native --lifecycle
7073
- if: startsWith(matrix.platform, 'macos-')
7174
run: bun run release:verify -- --homebrew

README.md

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -51,6 +51,8 @@ const running = await portreeve.withPort(
5151
- [Socket protocol](docs/protocol.md)
5252
- [CLI automation contract](docs/cli-contract.md)
5353
- [Stack definitions](docs/stacks.md)
54+
- [Desktop application](docs/desktop.md)
55+
- [Mixed process and Docker example](examples/mixed-stack/README.md)
5456
- [Migration from project-local remapping](docs/migration.md)
5557
- [Safety model](docs/safety.md)
5658
- [Troubleshooting](docs/troubleshooting.md)

apps/desktop/README.md

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,8 @@
11
# Portreeve Desktop engineering slice
22

3+
For the operator-facing behavior and security boundary, see the public
4+
[`docs/desktop.md`](../../docs/desktop.md) guide.
5+
36
This private workspace contains the non-shipping Electron MVP for Portreeve Desktop. It
47
displays lifecycle evidence from an exact Portreeve CLI executable, global inventory and
58
stack coordination evidence from the official JavaScript client, and confirmed

docs/desktop.md

Lines changed: 97 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,97 @@
1+
# Desktop application
2+
3+
Portreeve Desktop is the graphical inspection and coordination surface for the same
4+
per-user Portreeve installation managed by the CLI. It does not install a second server,
5+
keep a separate registry, or bypass the HTTP/JSON Unix-socket protocol. The application
6+
bundles a verified CLI artifact and can install that artifact into the one managed
7+
per-user location used by native supervision.
8+
9+
## Overview and lifecycle
10+
11+
The Overview tab reports the desktop, bundled CLI, managed CLI, running-server,
12+
supervisor, and socket layers independently. Lifecycle actions invoke an exact bundled
13+
or managed executable from the Electron main process; the renderer cannot choose an
14+
executable, run a shell command, or search `PATH`.
15+
16+
Available actions follow current evidence:
17+
18+
- install and start the managed Portreeve service;
19+
- start, stop, or restart native per-user supervision;
20+
- stop an explicitly manual `portreeve serve` process without adopting it;
21+
- upgrade the managed CLI after version and artifact verification;
22+
- uninstall supervision while retaining Portreeve data;
23+
- preview and confirm a complete Portreeve data reset.
24+
25+
Install, reset, and upgrade decisions remain explicit. Update discovery only reports a
26+
new desktop release and opens one fixed project download page after confirmation; it
27+
does not download or install updates automatically.
28+
29+
Lifecycle operations display a stable outcome plus safe structured error codes and
30+
messages. For example, an unsafe supervisor log mode is shown as an actionable
31+
permission failure rather than only `internal`. Unstructured errors are generalized so
32+
arbitrary exception detail does not become renderer content.
33+
34+
## Ports
35+
36+
The Ports tab uses the official JavaScript client to show global claimed and unclaimed
37+
TCP listeners. The main process reduces inventory before publishing it to the renderer.
38+
Claims show project, component, endpoint, worktree basename, mode, and timing; listeners
39+
show reduced ownership and process evidence. Lease tokens, internal database fields, and
40+
arbitrary executable paths are not exposed.
41+
42+
## Stacks
43+
44+
The Stacks tab reads definitions and current generation, activation, resolution, and
45+
fresh provider evidence through the official client. It supports:
46+
47+
- selecting and applying a checked-in `portreeve.stack.json` through the native file
48+
picker;
49+
- preparing or reusing one complete allocation generation;
50+
- inspecting components, endpoints, dependencies, placements, host addresses,
51+
Docker-network addresses, and provider evidence;
52+
- copying individual addresses and previewing a component-scoped sandbox discovery
53+
document for a launcher-supplied gateway;
54+
- explicitly reconciling provider evidence after launcher loss;
55+
- requesting evidence-gated activation ending after the project launcher stops its
56+
providers;
57+
- previewing seven-day missing-worktree stack pruning and typing `PRUNE` before
58+
execution.
59+
60+
Portreeve Desktop never starts or stops a project process or container, invokes Docker
61+
Compose, owns application startup order, maps project environment variables, or asserts
62+
application health. Those remain responsibilities of the project launcher. Stale stack
63+
evidence remains visible for diagnosis but withholds stack mutation controls until
64+
current evidence returns; the server revalidates every requested action as final
65+
authority.
66+
67+
## Trust and data boundary
68+
69+
Electron runs a sandboxed renderer with context isolation, Node integration disabled,
70+
and a restrictive local content policy. The preload exposes only named, schema-validated
71+
capabilities. The main process accepts IPC only from the primary `app://portreeve`
72+
renderer frame and owns the native file picker, clipboard write, fixed download-page
73+
navigation, exact CLI execution, and official client connection.
74+
75+
The desktop receives no general filesystem, shell, network-navigation, SQLite, Docker,
76+
or Portreeve-socket capability. Stack view models omit full worktree paths, claim and
77+
lease identifiers, run identifiers, Docker labels, and credentials. Discovery previews
78+
contain only their documented component-scoped address contract.
79+
80+
## Local package
81+
82+
Build and open an unsigned local macOS application bundle with the repository-pinned Bun
83+
toolchain:
84+
85+
```sh
86+
PORTREEVE_HOMEPAGE_URL=https://github.com/TrentBrown/portreeve \
87+
PORTREEVE_RELEASE_BASE_URL=https://github.com/TrentBrown/portreeve/releases/download \
88+
bun run release:build
89+
90+
bun run desktop:package
91+
open dist/desktop/Portreeve-darwin-arm64/Portreeve.app
92+
```
93+
94+
The packaging script selects the physical host architecture and verifies the bundled
95+
CLI against the generated release manifest and SHA-256 digest. This local bundle is a
96+
release candidate for manual verification, not a signed or notarized public desktop
97+
distribution.

docs/installation.md

Lines changed: 13 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -58,8 +58,18 @@ bun run release:build
5858

5959
bun run release:verify -- --native --lifecycle
6060
bun run release:verify -- --homebrew
61+
bun run stacks:verify
6162
```
6263

64+
`stacks:verify` is a destructive-only-to-its-own-fixtures native integration smoke. It
65+
creates one uniquely named disposable Docker container and one temporary process
66+
listener, drives a mixed stack through the official JavaScript client, and removes its
67+
container, worktree, Portreeve home, and runtime files in `finally` cleanup. It pulls
68+
`node:22.17.0-bookworm` when that default image is absent. Override the trusted Docker
69+
CLI, image, or launcher-rendered sandbox gateway with
70+
`PORTREEVE_DOCKER_EXECUTABLE`, `PORTREEVE_DOCKER_SMOKE_IMAGE`, or
71+
`PORTREEVE_SANDBOX_GATEWAY`.
72+
6373
`release:verify -- --native` verifies every artifact's checksum and executable
6474
header, validates the formula syntax, and actually runs the artifact matching
6575
the current machine. Adding `--lifecycle` uses a unique temporary native
@@ -71,7 +81,9 @@ gate and both native release smokes before publication. Cross-compilation alone
7181
is not release evidence. Native jobs require Node.js 22, Bun 1.3.14, Git,
7282
`lsof`, `ps`, Ruby, and a non-root login session. Linux runners must provide a
7383
working `systemd --user` manager. The Linux ARM64 job runs natively on GitHub's
74-
hosted `ubuntu-24.04-arm` image.
84+
hosted `ubuntu-24.04-arm` image. Both Linux release jobs also run the real mixed-stack
85+
Docker smoke. Docker Desktop on macOS is verified manually because hosted macOS runners
86+
do not provide the product's Docker Desktop environment.
7587

7688
Development may occur while a GitHub repository is private, and branch or
7789
manual Actions runs still work. The Portreeve repository is public before its

docs/issues/tb-portreeve-stacks/issues.md

Lines changed: 8 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -108,7 +108,7 @@ human review and merge.
108108

109109
## I-5 - Complete activation recovery and safe stack pruning
110110

111-
- **Status:** in-review
111+
- **Status:** closed
112112
- **Estimate:** 2d
113113
- **Plan steps:** P6
114114
- **Rubric criteria:** R6
@@ -161,9 +161,12 @@ stack-status contract for trusted inspection surfaces, and packaged macOS verifi
161161
The desktop remains a Portreeve coordinator rather than a project process or container
162162
orchestrator.
163163

164+
**Merged 2026-08-07.** PR #12 merged to `main` as `0fc3865` after the complete
165+
desktop, protocol, client, CLI, package, and boundary gates passed.
166+
164167
## I-7 - Complete integrated verification and feature evidence
165168

166-
- **Status:** open
169+
- **Status:** in-progress
167170
- **Estimate:** 2d
168171
- **Plan steps:** P8
169172
- **Rubric criteria:** R1, R2, R3, R4, R5, R6, R7, R8
@@ -173,3 +176,6 @@ orchestrator.
173176
Finish public documentation and representative examples, run mixed-stack and legacy
174177
end-to-end matrices on supported native environments and the packaged desktop, execute
175178
every workflow gate, and preserve the feature-final evidence and completion report.
179+
180+
**Started 2026-08-07.** Final sequential delivery branch
181+
`tb-portreeve-stacks-07-feature-final` begins from merged `main` at `0fc3865`.

docs/issues/tb-portreeve-stacks/scratchpad.md

Lines changed: 15 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -225,3 +225,18 @@ Add a read-only stack status operation under the existing stack-activations-v1 c
225225

226226
**Alternatives considered:**
227227
Reconstruct status from history in the desktop - rejected because history is an audit trail rather than a current-state index; add database access to the desktop - rejected by the existing trust boundary; show only definitions and prepared results from the current desktop session - rejected because the desktop must inspect stacks created by project launchers and survive restarts.
228+
229+
## [11] Make the assembled mixed-stack smoke launcher-owned and release-gated
230+
231+
[ ] **Promote**
232+
233+
**Confidence:** HIGH
234+
235+
**Blast Radius:** Native verification script, package scripts, Linux release jobs, mixed-stack example, and feature-final evidence
236+
237+
Add one explicit native verification harness that acts as a temporary trusted project launcher. It starts a disposable process listener and a uniquely named disposable Docker container, but it drives Portreeve exclusively through the official client and validates apply, prepare, mixed activation confirmation, scoped resolution, redacted snapshot publication and reading, current status, live-provider refusal, reconciliation, ending, pruning, and retained history. It owns exact cleanup for everything it creates. Run it manually on macOS Docker Desktop and in both Linux native release jobs; keep it out of the ordinary source test command because Docker is an optional runtime capability.
238+
239+
**Triggered by:** P8 requires one representative process-plus-Docker lifecycle across every public coordination phase without transferring project lifecycle authority to Portreeve
240+
241+
**Alternatives considered:**
242+
Extend Portreeve itself with a stack launcher - rejected because application lifecycle remains project-owned; rely only on isolated deterministic tests - rejected because P8 requires assembled native evidence; run the native smoke on every macOS GitHub runner - rejected because hosted macOS runners do not provide the Docker Desktop environment required by the product contract

docs/issues/tb-portreeve-stacks/tracker.md

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -103,7 +103,7 @@ Append PR boundary entries here.
103103
### PR #12 - Desktop stack coordination
104104

105105
- **PR:** [#12](https://github.com/TrentBrown/portreeve/pull/12)
106-
- **Status:** in review
106+
- **Status:** merged
107107
- **Scope:** P7 desktop Stacks views, definition apply, preparation, reconciliation,
108108
evidence-gated activation ending, snapshot/address copy, previewed confirmed pruning,
109109
actionable failures, and the aggregate read-only stack-status surface used by the
@@ -115,3 +115,4 @@ Append PR boundary entries here.
115115
test stack and claims were pruned. Independent judge: PASS WITH CONCERNS; code review:
116116
PASS with no remaining findings. Native Linux and assembled mixed-stack release
117117
verification remain I-7/P8.
118+
- **Merged:** `0fc3865` on 2026-08-07.

docs/stacks.md

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -34,6 +34,10 @@ The checked-in definition is `portreeve.stack.json` at the worktree root:
3434
}
3535
```
3636

37+
The repository includes a complete
38+
[mixed process and Docker launcher example](../examples/mixed-stack/README.md) that
39+
shows how one project-owned launcher consumes the returned leases and addresses.
40+
3741
The schema is strict. Unknown fields are rejected. Component and endpoint names are
3842
stable logical identities. An omitted dependency endpoint means `default`. Endpoint
3943
defaults are TCP, published, and required. Use `publish: false` for an endpoint that

examples/mixed-stack/README.md

Lines changed: 82 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,82 @@
1+
# Mixed process and Docker stack
2+
3+
This example models a process-backed `website` and Docker-backed `api` as one
4+
independently runnable worktree stack. Portreeve allocates both host publications and
5+
verifies their evidence. The project launcher still owns the website child process,
6+
Docker or Compose invocation, environment injection, startup order, health checks, and
7+
shutdown.
8+
9+
Copy [`portreeve.stack.json`](portreeve.stack.json) to the root of a disposable worktree
10+
or adapt its project and component names there. The file declares topology and preferred
11+
ports only; it contains no command, secret, environment value, or lease credential.
12+
13+
## Launcher sequence
14+
15+
1. Apply and prepare before deriving any environment or Compose override:
16+
17+
```sh
18+
portreeve stacks apply --json
19+
portreeve stacks prepare STACK_ID --json
20+
```
21+
22+
2. Begin one mixed activation. The API receives a Docker lease and the website receives
23+
a process lease:
24+
25+
```sh
26+
portreeve stacks begin GENERATION_ID --docker-component api --json
27+
```
28+
29+
3. The trusted launcher writes private lease output to a mode-`0600` runtime file. It
30+
starts the API container with every returned `requiredLabels` entry and publishes
31+
`127.0.0.1:API_HOST_PORT:3000`. It confirms the exact container ID with
32+
`stacks confirm-docker`.
33+
34+
4. The launcher starts the website on its returned host port, sets its backend URL from
35+
the resolved `backend` alias, and confirms the website root PID with
36+
`stacks confirm`:
37+
38+
```sh
39+
portreeve stacks resolve ACTIVATION_ID --component website --json
40+
```
41+
42+
A typical environment mapping is:
43+
44+
```text
45+
PORT=website.own.http.host.port
46+
API_URL=http://website.dependencies.backend.host.host:website.dependencies.backend.host.port
47+
```
48+
49+
Resolution describes network addresses, not startup readiness. The launcher decides
50+
when the API is healthy enough to start the website.
51+
52+
5. For a Docker or Codex sandbox, the host launcher renders a separate read-only
53+
discovery file instead of mounting the Portreeve socket:
54+
55+
```sh
56+
portreeve stacks snapshot ACTIVATION_ID \
57+
--component website \
58+
--gateway-host host.docker.internal \
59+
--file .portreeve/runtime/website-endpoints.json
60+
```
61+
62+
Linux launchers supply their configured host-gateway name or address. Sandboxed code
63+
reads the mounted file through `readEndpointSnapshot` or `PORTREEVE_ENDPOINTS_FILE`.
64+
65+
6. On shutdown, the launcher stops the website process and API container first, then
66+
requests `stacks end`. After a launcher crash, its replacement runs
67+
`stacks reconcile` and acts on `active`, `gone`, or `unknown` provider evidence
68+
rather than trusting the old launcher PID.
69+
70+
## Native assembled verification
71+
72+
Repository contributors with Docker available can run:
73+
74+
```sh
75+
bun run stacks:verify
76+
```
77+
78+
That harness creates a uniquely named disposable container and process listener and
79+
drives this complete lifecycle through the official JavaScript client: apply, prepare,
80+
begin, both confirmations, status, resolution, sandbox snapshot write/read, live end
81+
refusal, reconciliation, end, missing-worktree prune, and retained-history inspection.
82+
It removes every process, container, worktree, and Portreeve data path it creates.

0 commit comments

Comments
 (0)