Skip to content

[2126 by GPT-5.6 Sol] Restore real Linux setup wizard in the current desktop UI - #2132

Merged
integry merged 6 commits into
1950-epic-cross-platform-dskfrom
2126/gpt-5.6-sol-restore-real-linux-setup--20260906-0949-v1e
Sep 6, 2026
Merged

[2126 by GPT-5.6 Sol] Restore real Linux setup wizard in the current desktop UI#2132
integry merged 6 commits into
1950-epic-cross-platform-dskfrom
2126/gpt-5.6-sol-restore-real-linux-setup--20260906-0949-v1e

Conversation

@propr-dev

@propr-dev propr-dev Bot commented Sep 6, 2026

Copy link
Copy Markdown
Contributor

AI Implementation Summary

Closes #2126

Branch: 2126/gpt-5.6-sol-restore-real-linux-setup--20260906-0949-v1e
Commits: ✅ Changes committed (8259e3c)


AI Processing Completed

Execution Details:

Summary:
Implemented the real Linux local setup journey.

Key changes:

  • Added the guided setup wizard with prerequisites, configuration, progress, cancellation, retry, recovery, and completion.
  • Wired the real @propr/local-setup engine through main-process IPC.
  • Added fixed-root filesystem authority, opaque private-key capabilities, native secret prompting, validation, persistence, and redaction.
  • Routed successful setup through the existing strict probe, pairing, activation, CSP, and profile flow.
  • Kept macOS and Windows remote-only.
  • Included setup resources in Linux packages and integrated shutdown cancellation.
  • Added focused controller, preload, adapter, cancellation/retry, and authenticated-handoff tests.

Verification:

  • Desktop and UI typechecks pass.
  • Focused UI tests: 18 passed.
  • Controller/preload/release tests: 33 passed.
  • Linux Electron package build succeeds with required setup resources present.
  • Full desktop suite’s sole resource-policy failure was corrected; its affected suite passes 24/24.
  • Captured focused visual preview evidence.
  • No real local ProPR stack was installed or modified.

Detailed Logs:

  • Conversation: 176 messages
  • Session: 01a07620-1bad-7110-be05-85619d4af603

Log files stored at:

  • conversation: /tmp/claude-logs/issue-2126-2026-09-06T10-14-52-858Z-conversation.json
  • output: /tmp/claude-logs/issue-2126-2026-09-06T10-14-52-858Z-output.txt
Latest Conversation Messages

This PR was created automatically by ProPR after processing issue #2126.


💡 Need changes?

Comment on this PR to request refinements — the AI agent monitors comments and will update the implementation based on your feedback. Keep iterating until you're satisfied!


Visual preview

Linux setup prerequisites

Linux setup prerequisites

The production desktop entry opens the restored guided Linux setup journey at a 1280×820 desktop viewport.

… current des

Implemented by ProPR AI using gpt-5.6-sol model.

Implementation completed successfully.
@integry

integry commented Sep 6, 2026

Copy link
Copy Markdown
Owner

/ultrafix goal=8 max=1

@propr-dev propr-dev Bot added the ultrafix label Sep 6, 2026
@propr-dev

propr-dev Bot commented Sep 6, 2026

Copy link
Copy Markdown
Contributor Author

🔄 Ultrafix loop started (goal: 8/10, max cycles: 1)

First action: /review

💡 Tip: Remove the ultrafix label from this PR to stop further ultrafix cycles.

@integry

integry commented Sep 6, 2026

Copy link
Copy Markdown
Owner

/review

@propr-dev

propr-dev Bot commented Sep 6, 2026

Copy link
Copy Markdown
Contributor Author

AI Code Review Complete requested by @integry

Posted 1 review:

View Task Details

@integry

integry commented Sep 6, 2026

Copy link
Copy Markdown
Owner

Independent review + current CI correction request for exact head 8259e3c. Finish the currently running Ultrafix review first, then address demonstrated blockers together in one focused pass:

  1. First-run GUI authentication: setup-controller.ts automatically approves GitHub login and selected agent logins. createDesktopSetupHost uses CLI default actions; hostActions invokes loginWithGithubCli(interactive:true), which spawnSyncs gh auth login with inherited stdio. Agent login similarly invokes synchronous Docker login with inherited stdio. A desktop launch has no usable terminal and this blocks Electron main/cancel/shutdown IPC. Provide a real desktop-visible, asynchronous and cancellable authentication handoff for fresh users, preserving the complete setup journey (not silently skipping authentication or replacing the wizard with a terminal prerequisite). Add tests through the real host-action boundary for unauthenticated startup and cancellation, without using real credentials or installing a host stack.

  2. Actual macOS package CI failure: run34026886515 job101469357351, setup-controller.test.ts cancellation/retry test rejects with SetupCapabilityError: The setup directory must be a real directory. Stack setup-capabilities.ts:28 -> open:53 -> controller begin:118 -> test:41. Check native temp-directory aliases and canonicalize the fixture appropriately; preserve production symlink/path protections. Require Mac/Linux relevant CI, Windows remains deferred.

  3. User-visible recovery: LocalSetupWizard adapter.status rejection sets error but the !snapshot branch permanently renders Loading setup, hiding both the error and a way back/retry. Running cancellation promise has no rejection handler; recovery retry failure also sets an error that Recovery never renders. Handle these failures visibly and keep retry/back available, with regression tests.

Independent merge inspection confirms rollback finally reconciliation, ProfileStore v3, exact-origin CSP and normal save/probe/pair/activate are preserved; retain them. No workflow diff was introduced, so do not chase the six-target CI display as a new profile regression. Leave PR open for verification and integrate only into desktop epic, never main.

@github-actions

github-actions Bot commented Sep 6, 2026

Copy link
Copy Markdown

Checks Failed

Validation failed during setup, tests, CLI packaging, linting, or build checks.

View Logs
Build check diagnostics for run 34026886517, attempt 1
Checkout outcome: success

--- GitHub Actions workflow lint ---
docker.io/rhysd/actionlint@sha256:b1934ee5f1c509618f2508e6eb47ee0d3520686341fec936f3b79331f9315667: Pulling from rhysd/actionlint
589002ba0eae: Pulling fs layer
c09af8888d6a: Pulling fs layer
4ccd7d76ca98: Pulling fs layer
b6b4b7b0e257: Pulling fs layer
b6b4b7b0e257: Waiting
4ccd7d76ca98: Verifying Checksum
4ccd7d76ca98: Download complete
c09af8888d6a: Verifying Checksum
c09af8888d6a: Download complete
589002ba0eae: Verifying Checksum
589002ba0eae: Download complete
b6b4b7b0e257: Verifying Checksum
b6b4b7b0e257: Download complete
589002ba0eae: Pull complete
c09af8888d6a: Pull complete
4ccd7d76ca98: Pull complete
b6b4b7b0e257: Pull complete
Digest: sha256:b1934ee5f1c509618f2508e6eb47ee0d3520686341fec936f3b79331f9315667
Status: Downloaded newer image for rhysd/actionlint@sha256:b1934ee5f1c509618f2508e6eb47ee0d3520686341fec936f3b79331f9315667
docker.io/rhysd/actionlint@sha256:b1934ee5f1c509618f2508e6eb47ee0d3520686341fec936f3b79331f9315667

--- Release shell script lint ---

--- Toolchain ---
v22.23.2
10.9.8

--- Dependency installation ---
npm warn deprecated inflight@1.0.6: This module is not supported, and leaks memory. Do not use it. Check out lru-cache if you want a good and tested way to coalesce async requests by a key value, which is much more comprehensive and powerful.
npm warn deprecated gar@1.0.4: Package no longer supported. Contact Support at https://www.npmjs.com/support for more info.
npm warn deprecated glob@7.2.3: Old versions of glob are not supported, and contain widely publicized security vulnerabilities, which have been fixed in the current version. Please update. Support for old versions may be purchased (at exorbitant rates) by contacting i@izs.me

added 1124 packages, and audited 1133 packages in 21s

323 packages are looking for funding
  run `npm fund` for details

found 0 vulnerabilities

--- Fast unit tests ---
Running fast unit tests (no Redis/Docker dependencies)...

> propr@0.8.15 pretest:unit
> npm run build -w @propr/shared && npm run build -w @propr/local-setup


> @propr/shared@0.8.15 build
> tsc


> @propr/local-setup@0.8.15 build
> tsc


> propr@0.8.15 test:unit
> NODE_ENV=test npx tsx --experimental-test-module-mocks --test test/minimal.test.ts test/modelName.test.ts test/agentContainerResources.test.ts test/agentDockerfileSupplyChain.test.ts test/agentImagePreparation.test.ts test/daemonEventIntake.test.ts test/databaseMigrationGate.test.ts test/deployPrPreview.test.mjs test/generateContext.test.ts test/githubEventIntakeMode.test.ts test/intakeModePrerequisites.test.ts test/orchestratorMigrationPhase.test.mjs test/validateRoutingUrl.test.ts test/routingWebSocketProtocol.test.ts test/routingWebSocketIntakeService.test.ts test/routingStatusPublisher.test.ts test/releaseValidation.test.mjs test/sessionSecret.test.ts test/testSuiteRunner.test.mjs packages/api/test/connectAuth.test.ts packages/api/test/attachmentUploadCleanup.test.ts packages/api/test/configReloadSubscription.test.ts packages/api/test/desktopApiBoundary.test.ts packages/api/test/dockerCommandSafety.test.ts packages/api/test/listenAddress.test.ts packages/api/test/oauthState.test.ts packages/api/test/requestRateLimits.test.ts packages/api/test/statusRoutes.test.ts packages/api/test/agentRuntimeRoutes.test.ts packages/api/test/instanceAuthorization.test.ts packages/api/test/routeAuthorization.test.ts

TAP version 13
# Subtest: agent runtime package routes
    # Subtest: queues one validated package profile for the unified agent image
    ok 1 - queues one validated package profile for the unified agent image
      ---
      duration_ms: 6.139747
      type: 'test'
      ...
    # Subtest: persists a failed state when queue submission fails
    ok 2 - persists a failed state when queue submission fails
      ---
      duration_ms: 1.260201
      type: 'test'
      ...
    # Subtest: rejects a package missing from an effective runtime before queueing
    ok 3 - rejects a package missing from an effective runtime before queueing
      ---
      duration_ms: 1.072903
      type: 'test'
      ...
    # Subtest: times out slow runtime package validation before queueing
    ok 4 - times out slow runtime package validation before queueing
      ---
      duration_ms: 8.07464
      type: 'test'
      ...
    # Subtest: returns package suggestions from configured runtimes
    ok 5 - returns package suggestions from configured runtimes
      ---
      duration_ms: 0.879996
      type: 'test'
      ...
    # Subtest: enforces PROPR_ADMIN_USERS when configured
    ok 6 - enforces PROPR_ADMIN_USERS when configured
      ---
      duration_ms: 0.487564
      type: 'test'
      ...
    # Subtest: denies runtime package changes by default when no admin policy is configured
    ok 7 - denies runtime package changes by default when no admin policy is configured
      ---
      duration_ms: 0.428947
      type: 'test'
      ...
    # Subtest: requires runtime-management permission before returning runtime package state
    ok 8 - requires runtime-management permission before returning runtime package state
      ---
      duration_ms: 0.493964
      type: 'test'
      ...
    # Subtest: warms the package catalog when an admin loads runtime package state
    ok 9 - warms the package catalog when an admin loads runtime package state
      ---
      duration_ms: 0.809843
      type: 'test'
      ...
    # Subtest: does not warm the package catalog when a member is rejected
    ok 10 - does not warm the package catalog when a member is rejected
      ---
      duration_ms: 3.688767
      type: 'test'
      ...
    # Subtest: resolves the runtime build queue lazily when queueing
    ok 11 - resolves the runtime build queue lazily when queueing
      ---
      duration_ms: 0.985944
      type: 'test'
      ...
    # Subtest: reports apply load failures through the route response
    ok 12 - reports apply load failures through the route response
      ---
      duration_ms: 0.504649
      type: 'test'
      ...
    # Subtest: verifies the current effective images through the runtime-management boundary
    ok 13 - verifies the current effective images through the runtime-management boundary
      ---
      duration_ms: 0.665307
      type: 'test'
      ...
    # Subtest: rejects runtime verification without runtime-management permission
    ok 14 - rejects runtime verification without runtime-management permission
      ---
      duration_ms: 0.463608
      type: 'test'
      ...
    1..14
ok 1 - agent runtime package routes
  ---
  duration_ms: 28.30605
  type: 'suite'
  ...
# [2026-09-06 10:15:38.199 +0000] �[32mINFO�[39m: �[36mSQLite database connection established successfully�[39m
#     filename: "/home/runner/work/propr/propr/data/propr.test.sqlite"
#     environment: "test"
# [2026-09-06 10:15:38.224 +0000] �[32mINFO�[39m: �[36mSQLite database connection closed�[39m
# Subtest: upload handler removes Multer files rejected before attachment processing
ok 2 - upload handler removes Multer files rejected before attachment processing
  ---
  duration_ms: 10.428915
  type: 'test'
  ...
# Subtest: attachment processing removes temporary and final files when persistence fails
ok 3 - attachment processing removes temporary and final files when persistence fails
  ---
  duration_ms: 13.158578
  type: 'test'
  ...
# Subtest: temporary cleanup refuses paths outside its configured root
ok 4 - temporary cleanup refuses paths outside its configured root
  ---
  duration_ms: 2.983209
  type: 'test'
  ...
# Subtest: temporary cleanup rejects dot-segment paths that resolve above its root
ok 5 - temporary cleanup rejects dot-segment paths that resolve above its root
  ---
  duration_ms: 0.718326
  type: 'test'
  ...
# Subtest: attachment processing rejects path-like draft IDs and still cleans its temp file
ok 6 - attachment processing rejects path-like draft IDs and still cleans its temp file
  ---
  duration_ms: 8.243751
  type: 'test'
  ...
# [2026-09-06 10:15:37.735 +0000] �[32mINFO�[39m: �[36mCreated data directory for SQLite database�[39m
#     directory: "/home/runner/work/propr/propr/data"
# [2026-09-06 10:15:38.196 +0000] �[32mINFO�[39m: �[36mSQLite database connection established successfully�[39m
#     filename: "/home/runner/work/propr/propr/data/propr.test.sqlite"
#     environment: "test"
# [2026-09-06 10:15:38.231 +0000] �[32mINFO�[39m: �[36mSQLite database connection closed�[39m
# Subtest: API config subscription serializes the startup reload with settings updates
ok 7 - API config subscription serializes the startup reload with settings updates
  ---
  duration_ms: 10.694277
  type: 'test'
  ...
# Subtest: API config subscription reloads repository updates
ok 8 - API config subscription reloads repository updates
  ---
  duration_ms: 0.589215
  type: 'test'
  ...
# Subtest: API config subscription ignores unrelated and malformed events
ok 9 - API config subscription ignores unrelated and malformed events
  ---
  duration_ms: 0.411932
  type: 'test'
  ...
# Subtest: a failed API settings reload does not block the next notification
ok 10 - a failed API settings reload does not block the next notification
  ---
  duration_ms: 3.254004
  type: 'test'
  ...
# Subtest: relay tunnel mode uses Connect without local OAuth credentials
ok 11 - relay tunnel mode uses Connect without local OAuth credentials
  ---
  duration_ms: 16.313885
  type: 'test'
  ...
# Subtest: relay tunnel mode preserves Connect for operator-supplied service endpoints
ok 12 - relay tunnel mode preserves Connect for operator-supplied service endpoints
  ---
  duration_ms: 0.339063
  type: 'test'
  ...
# Subtest: local relay mode uses Connect without a per-instance OAuth App
ok 13 - local relay mode uses Connect without a per-instance OAuth App
  ---
  duration_ms: 0.69966
  type: 'test'
  ...
# Subtest: off-tunnel relay inference uses the shared canonical loopback rule
ok 14 - off-tunnel relay inference uses the shared canonical loopback rule
  ---
  duration_ms: 11.701934
  type: 'test'
  ...
# Subtest: off-tunnel custom relay enrollment does not infer hosted Connect auth
ok 15 - off-tunnel custom relay enrollment does not infer hosted Connect auth
  ---
  duration_ms: 0.283732
  type: 'test'
  ...
# Subtest: literal example OAuth placeholders do not enable GitHub web auth
ok 16 - literal example OAuth placeholders do not enable GitHub web auth
  ---
  duration_ms: 0.190052
  type: 'test'
  ...
# Subtest: explicit custom GitHub web auth remains supported
ok 17 - explicit custom GitHub web auth remains supported
  ---
  duration_ms: 0.149702
  type: 'test'
  ...
# Subtest: explicit custom GitHub web auth wins over relay inference off-tunnel
ok 18 - explicit custom GitHub web auth wins over relay inference off-tunnel
  ---
  duration_ms: 0.228349
  type: 'test'
  ...
# Subtest: Connect authorization URL carries the exact callback and CSRF state
ok 19 - Connect authorization URL carries the exact callback and CSRF state
  ---
  duration_ms: 0.854639
  type: 'test'
  ...
# Subtest: Connect authorization URL rejects configured query strings and fragments
ok 20 - Connect authorization URL rejects configured query strings and fragments
  ---
  duration_ms: 10.321262
  type: 'test'
  ...
# Subtest: redeems a Connect code server-to-server without exposing the relay token in the body
ok 21 - redeems a Connect code server-to-server without exposing the relay token in the body
  ---
  duration_ms: 86.828151
  type: 'test'
  ...
# Subtest: binds the Connect identity username to the validated token owner
ok 22 - binds the Connect identity username to the validated token owner
  ---
  duration_ms: 1.202296
  type: 'test'
  ...
# Subtest: preserves expiring OAuth grant fields returned by Connect
ok 23 - preserves expiring OAuth grant fields returned by Connect
  ---
  duration_ms: 1.129658
  type: 'test'
  ...
# [2026-09-06 10:15:39.399 +0000] �[32mINFO�[39m: �[36mSQLite database connection established successfully�[39m
#     filename: "/home/runner/work/propr/propr/data/propr.test.sqlite"
#     environment: "test"
# Subtest: assembled desktop API authentication boundary
    # Subtest: keeps discovery and bounded pairing bootstrap ahead of the operational API guard
    ok 1 - keeps discovery and bounded pairing bootstrap ahead of the operational API guard
      ---
      duration_ms: 161.887378
      type: 'test'
      ...
    1..1
ok 24 - assembled desktop API authentication boundary
  ---
  duration_ms: 163.188841
  type: 'suite'
  ...
# [2026-09-06 10:15:39.548 +0000] �[32mINFO�[39m: �[36mSQLite database connection closed�[39m
# Subtest: accepts Docker IDs and ProPR-generated container names
ok 25 - accepts Docker IDs and ProPR-generated container names
  ---
  duration_ms: 1.073634
  type: 'test'
  ...
# Subtest: rejects container references that could be parsed as options or shell syntax
ok 26 - rejects container references that could be parsed as options or shell syntax
  ---
  duration_ms: 0.206136
  type: 'test'
  ...
# Subtest: production subprocess call sites do not invoke a command shell
ok 27 - production subprocess call sites do not invoke a command shell
  ---
  duration_ms: 135.181275
  type: 'test'
  ...
# Subtest: no-shell guard rejects aliases and enabled shell options
ok 28 - no-shell guard rejects aliases and enabled shell options
  ---
  duration_ms: 3.230888
  type: 'test'
  ...
# Subtest: instance authorization
    # Subtest: defaults unassigned authenticated users to members on a new installation
    ok 1 - defaults unassigned authenticated users to members on a new installation
      ---
      duration_ms: 16.637175
      type: 'test'
      ...
    # Subtest: refuses startup until a bootstrap or durable administrator exists
    ok 2 - refuses startup until a bootstrap or durable administrator exists
      ---
      duration_ms: 7.186021
      type: 'test'
      ...
    # Subtest: grants full admin permissions to PROPR_ADMIN_USERS
    ok 3 - grants full admin permissions to PROPR_ADMIN_USERS
      ---
      duration_ms: 3.401271
      type: 'test'
      ...
    # Subtest: resolves durable roles by numeric GitHub ID after a username change
    ok 4 - resolves durable roles by numeric GitHub ID after a username change
      ---
      duration_ms: 4.269149
      type: 'test'
      ...
    # Subtest: resolves demo users without installation permissions
    ok 5 - resolves demo users without installation permissions
      ---
      duration_ms: 3.068036
      type: 'test'
      ...
    # Subtest: permission middleware rejects members with a stable error code
    ok 6 - permission middleware rejects members with a stable error code
      ---
      duration_ms: 4.44491
      type: 'test'
      ...
    # Subtest: current-user responses expose capabilities without OAuth credentials
    ok 7 - current-user responses expose capabilities without OAuth credentials
      ---
      duration_ms: 4.328608
      type: 'test'
      ...
    1..7
ok 29 - instance authorization
  ---
  duration_ms: 45.137576
  type: 'suite'
  ...
# Subtest: instance member service
    # Subtest: does not let an unassigned user claim the initial administrator role
    ok 1 - does not let an unassigned user claim the initial administrator role
      ---
      duration_ms: 7.920972
      type: 'test'
      ...
    # Subtest: stores a bootstrap administrator against their numeric GitHub ID
    ok 2 - stores a bootstrap administrator against their numeric GitHub ID
      ---
      duration_ms: 11.527668
      type: 'test'
      ...
    # Subtest: adds explicit members and writes an audit entry
    ok 3 - adds explicit members and writes an audit entry
      ---
      duration_ms: 10.14852
      type: 'test'
      ...
    # Subtest: keeps environment authority separate from durable role assignments
    ok 4 - keeps environment authority separate from durable role assignments
      ---
      duration_ms: 2.752337
      type: 'test'
      ...
    # Subtest: prevents removing the last durable administrator
    ok 5 - prevents removing the last durable administrator
      ---
      duration_ms: 8.971262
      type: 'test'
      ...
    # Subtest: allows role changes once another administrator exists
    ok 6 - allows role changes once another administrator exists
      ---
      duration_ms: 8.4987
      type: 'test'
      ...
    1..6
ok 30 - instance member service
  ---
  duration_ms: 50.445027
  type: 'suite'
  ...
# Subtest: instance catalog
    # Subtest: returns operational agent and repository fields without installation internals
    ok 1 - returns operational agent and repository fields without installation internals
      ---
      duration_ms: 3.781114
      type: 'test'
      ...
    # Subtest: projects indexing status only for enabled repository and branch entries
    ok 2 - projects indexing status only for enabled repository and branch entries
      ---
      duration_ms: 2.554083
      type: 'test'
      ...
    1..2
ok 31 - instance catalog
  ---
  duration_ms: 6.476316
  type: 'suite'
  ...
# Subtest: instance admin routes
    # Subtest: resolves a GitHub username and creates one durable member assignment
    ok 1 - resolves a GitHub username and creates one durable member assignment
      ---
      duration_ms: 4.281368
      type: 'test'
      ...
    # Subtest: rejects a non-numeric audit limit with a 400
    ok 2 - rejects a non-numeric audit limit with a 400
      ---
      duration_ms: 2.328578
      type: 'test'
      ...
    # Subtest: rejects GitHub usernames with trailing or consecutive hyphens
    ok 3 - rejects GitHub usernames with trailing or consecutive hyphens
      ---
      duration_ms: 3.638563
      type: 'test'
      ...
    1..3
ok 32 - instance admin routes
  ---
  duration_ms: 10.430789
  type: 'suite'
  ...
# [2026-09-06 10:15:41.955 +0000] �[32mINFO�[39m: �[36mSQLite database connection established successfully�[39m
#     filename: "/home/runner/work/propr/propr/data/propr.test.sqlite"
#     environment: "test"
# [2026-09-06 10:15:42.060 +0000] �[32mINFO�[39m: �[36mSQLite database connection closed�[39m
# Subtest: direct API execution binds to loopback by default
ok 33 - direct API execution binds to loopback by default
  ---
  duration_ms: 1.278338
  type: 'test'
  ...
# Subtest: containerized API execution remains reachable through its published port
ok 34 - containerized API execution remains reachable through its published port
  ---
  duration_ms: 0.202
  type: 'test'
  ...
# Subtest: an explicit API listen host overrides environment defaults
ok 35 - an explicit API listen host overrides environment defaults
  ---
  duration_ms: 0.224744
  type: 'test'
  ...
# Subtest: GitHub OAuth authorization stores and sends a random state nonce
ok 36 - GitHub OAuth authorization stores and sends a random state nonce
  ---
  duration_ms: 3.745722
  type: 'test'
  ...
# Subtest: GitHub OAuth callback rejects a mismatched state before token exchange
ok 37 - GitHub OAuth callback rejects a mismatched state before token exchange
  ---
  duration_ms: 1.754635
  type: 'test'
  ...
# [2026-09-06 10:15:44.245 +0000] �[32mINFO�[39m: �[36mSQLite database connection established successfully�[39m
#     filename: "/home/runner/work/propr/propr/data/propr.test.sqlite"
#     environment: "test"
# [2026-09-06 10:15:44.246 +0000] �[32mINFO�[39m: �[36mSQLite database connection closed�[39m
# Subtest: returns a standard 429 response after the configured quota
ok 38 - returns a standard 429 response after the configured quota
  ---
  duration_ms: 147.734316
  type: 'test'
  ...
# Subtest: the real global API limiter keeps no-store headers when saturated
ok 39 - the real global API limiter keeps no-store headers when saturated
  ---
  duration_ms: 19.939859
  type: 'test'
  ...
# Subtest: route limiting, 503, and errors inherit the earliest API no-store boundary
ok 40 - route limiting, 503, and errors inherit the earliest API no-store boundary
  ---
  duration_ms: 28.943489
  type: 'test'
  ...
# Subtest: does not charge CORS preflight requests against the quota
ok 41 - does not charge CORS preflight requests against the quota
  ---
  duration_ms: 17.883276
  type: 'test'
  ...
# Subtest: route-level webhook limiting preserves alternate-case raw bodies and rejects excess requests before parsing
ok 42 - route-level webhook limiting preserves alternate-case raw bodies and rejects excess requests before parsing
  ---
  duration_ms: 27.513626
  type: 'test'
  ...
# Subtest: does not let an unconfigured private peer rotate quota buckets with X-Forwarded-For
ok 43 - does not let an unconfigured private peer rotate quota buckets with X-Forwarded-For
  ---
  duration_ms: 11.390454
  type: 'test'
  ...
# Subtest: tunnel trust does not let an unrelated private peer rotate quota buckets
ok 44 - tunnel trust does not let an unrelated private peer rotate quota buckets
  ---
  duration_ms: 14.479201
  type: 'test'
  ...
# Subtest: trusted TLS proxy preserves per-client quotas and secure session cookies
ok 45 - trusted TLS proxy preserves per-client quotas and secure session cookies
  ---
  duration_ms: 21.588318
  type: 'test'
  ...
# Subtest: resolves secure defaults and explicit positive-integer overrides
ok 46 - resolves secure defaults and explicit positive-integer overrides
  ---
  duration_ms: 0.673731
  type: 'test'
  ...
# Subtest: rejects invalid overrides instead of silently disabling protection
ok 47 - rejects invalid overrides instead of silently disabling protection
  ---
  duration_ms: 1.006004
  type: 'test'
  ...
# Subtest: assembled instance permission routes
    # Subtest: captures Express 5 named wildcard parameters as path segments
    ok 1 - captures Express 5 named wildcard parameters as path segments
      ---
      duration_ms: 37.224076
      type: 'test'
      ...
    # Subtest: members can read only the sanitized catalog endpoints
    ok 2 - members can read only the sanitized catalog endpoints
      ---
      duration_ms: 50.622121
      type: 'test'
      ...
# [2026-09-06 10:15:44.862 +0000] �[32mINFO�[39m: �[36mSQLite database connection established successfully�[39m
#     filename: "/home/runner/work/propr/propr/data/propr.test.sqlite"
#     environment: "test"
    # Subtest: administrators pass every management guard in the route matrix
    ok 3 - administrators pass every management guard in the route matrix
      ---
      duration_ms: 34.455354
      type: 'test'
      ...
    # Subtest: demo users can read only the synthetic Agent Tank usage feed
    ok 4 - demo users can read only the synthetic Agent Tank usage feed
      ---
      duration_ms: 7.810489
      type: 'test'
      ...
    1..4
ok 48 - assembled instance permission routes
  ---
  duration_ms: 131.319091
  type: 'suite'
  ...
# [2026-09-06 10:15:44.990 +0000] �[32mINFO�[39m: �[36mSQLite database connection closed�[39m
# Error loading summarization warnings: SqliteError: select * from `system_configs` where `key` = 'summarization_runtime_state' limit 1 - no such table: system_configs
#     at Database.prepare (/home/runner/work/propr/propr/node_modules/better-sqlite3/lib/methods/wrappers.js:5:21)
#     at Client_BetterSQLite3._query (/home/runner/work/propr/propr/node_modules/knex/lib/dialects/better-sqlite3/index.js:35:34)
#     at executeQuery (/home/runner/work/propr/propr/node_modules/knex/lib/execution/internal/query-executioner.js:37:17)
#     at Client_BetterSQLite3.query (/home/runner/work/propr/propr/node_modules/knex/lib/client.js:154:12)
#     at Runner.query (/home/runner/work/propr/propr/node_modules/knex/lib/execution/runner.js:141:36)
#     at ensureConnectionCallback (/home/runner/work/propr/propr/node_modules/knex/lib/execution/internal/ensure-connection-callback.js:13:17)
#     at Runner.ensureConnection (/home/runner/work/propr/propr/node_modules/knex/lib/execution/runner.js:318:20)
#     at async Runner.run (/home/runner/work/propr/propr/node_modules/knex/lib/execution/runner.js:30:19)
#     at async getConfigWithClient (/home/runner/work/propr/propr/packages/core/src/config/configStore.ts:17:24)
#     at async loadSummarizationRuntimeState (/home/runner/work/propr/propr/packages/core/src/config/configManagerSummarization.ts:101:19) {
#   code: 'SQLITE_ERROR'
# }
# Subtest: /api/status omits disabled configured agents
ok 49 - /api/status omits disabled configured agents
  ---
  duration_ms: 2278.32398
  type: 'test'
  ...
# Subtest: /api/compatibility returns public version contract metadata
ok 50 - /api/compatibility returns public version contract metadata
  ---
  duration_ms: 4.41105
  type: 'test'
  ...
# Subtest: /api/desktop/discovery returns the bounded public identity and runtime origin
ok 51 - /api/desktop/discovery returns the bounded public identity and runtime origin
  ---
  duration_ms: 4.108481
  type: 'test'
  ...
# Subtest: /api/desktop/discovery redacts identity persistence failures
ok 52 - /api/desktop/discovery redacts identity persistence failures
  ---
  duration_ms: 5.358688
  type: 'test'
  ...
# Error loading summarization warnings: SqliteError: select * from `system_configs` where `key` = 'summarization_runtime_state' limit 1 - no such table: system_configs
#     at Database.prepare (/home/runner/work/propr/propr/node_modules/better-sqlite3/lib/methods/wrappers.js:5:21)
#     at Client_BetterSQLite3._query (/home/runner/work/propr/propr/node_modules/knex/lib/dialects/better-sqlite3/index.js:35:34)
#     at executeQuery (/home/runner/work/propr/propr/node_modules/knex/lib/execution/internal/query-executioner.js:37:17)
#     at Client_BetterSQLite3.query (/home/runner/work/propr/propr/node_modules/knex/lib/client.js:154:12)
#     at Runner.query (/home/runner/work/propr/propr/node_modules/knex/lib/execution/runner.js:141:36)
#     at ensureConnectionCallback (/home/runner/work/propr/propr/node_modules/knex/lib/execution/internal/ensure-connection-callback.js:13:17)
#     at Runner.ensureConnection (/home/runner/work/propr/propr/node_modules/knex/lib/execution/runner.js:318:20)
#     at async Runner.run (/home/runner/work/propr/propr/node_modules/knex/lib/execution/runner.js:30:19)
#     at async getConfigWithClient (/home/runner/work/propr/propr/packages/core/src/config/configStore.ts:17:24)
#     at async loadSummarizationRuntimeState (/home/runner/work/propr/propr/packages/core/src/config/configManagerSummarization.ts:101:19) {
#   code: 'SQLITE_ERROR'
# }
# Subtest: /api/status returns default Claude fallback when no agents are configured
ok 53 - /api/status returns default Claude fallback when no agents are configured
  ---
  duration_ms: 3.047515
  type: 'test'
  ...
# Error loading summarization warnings: SqliteError: select * from `system_configs` where `key` = 'summarization_runtime_state' limit 1 - no such table: system_configs
#     at Database.prepare (/home/runner/work/propr/propr/node_modules/better-sqlite3/lib/methods/wrappers.js:5:21)
#     at Client_BetterSQLite3._query (/home/runner/work/propr/propr/node_modules/knex/lib/dialects/better-sqlite3/index.js:35:34)
#     at executeQuery (/home/runner/work/propr/propr/node_modules/knex/lib/execution/internal/query-executioner.js:37:17)
#     at Client_BetterSQLite3.query (/home/runner/work/propr/propr/node_modules/knex/lib/client.js:154:12)
#     at Runner.query (/home/runner/work/propr/propr/node_modules/knex/lib/execution/runner.js:141:36)
#     at ensureConnectionCallback (/home/runner/work/propr/propr/node_modules/knex/lib/execution/internal/ensure-connection-callback.js:13:17)
#     at Runner.ensureConnection (/home/runner/work/propr/propr/node_modules/knex/lib/execution/runner.js:318:20)
#     at async Runner.run (/home/runner/work/propr/propr/node_modules/knex/lib/execution/runner.js:30:19)
#     at async getConfigWithClient (/home/runner/work/propr/propr/packages/core/src/config/configStore.ts:17:24)
#     at async loadSummarizationRuntimeState (/home/runner/work/propr/propr/packages/core/src/config/configManagerSummarization.ts:101:19) {
#   code: 'SQLITE_ERROR'
# }
# [notifications] Failed to project system health snapshot: Error: notification persistence unavailable
#     at projectSystemSnapshot (/home/runner/work/propr/propr/packages/api/test/statusRoutes.test.ts:308:13)
#     at Object.getStatus (/home/runner/work/propr/propr/packages/api/routes/statusRoutes.ts:224:14)
#     at async readStatus (/home/runner/work/propr/propr/packages/api/test/statusRoutes.test.ts:186:3)
#     at async TestContext.<anonymous> (/home/runner/work/propr/propr/packages/api/test/statusRoutes.test.ts:305:16)
#     at async Test.run (node:internal/test_runner/test:1054:7)
#     at async Test.processPendingSubtests (node:internal/test_runner/test:744:7)
# Subtest: /api/status isolates system notification projection failures
ok 54 - /api/status isolates system notification projection failures
  ---
  duration_ms: 6.301567
  type: 'test'
  ...
# Subtest: /api/status surfaces unified agent image outages
ok 55 - /api/status surfaces unified agent image outages
  ---
  duration_ms: 1.480308
  type: 'test'
  ...
# Subtest: /api/status includes warnings field in demo mode
ok 56 - /api/status includes warnings field in demo mode
  ---
  duration_ms: 1.186572
  type: 'test'
  ...
# Error loading summarization warnings: SqliteError: select * from `system_configs` where `key` = 'summarization_runtime_state' limit 1 - no such table: system_configs
#     at Database.prepare (/home/runner/work/propr/propr/node_modules/better-sqlite3/lib/methods/wrappers.js:5:21)
#     at Client_BetterSQLite3._query (/home/runner/work/propr/propr/node_modules/knex/lib/dialects/better-sqlite3/index.js:35:34)
#     at executeQuery (/home/runner/work/propr/propr/node_modules/knex/lib/execution/internal/query-executioner.js:37:17)
#     at Client_BetterSQLite3.query (/home/runner/work/propr/propr/node_modules/knex/lib/client.js:154:12)
#     at Runner.query (/home/runner/work/propr/propr/node_modules/knex/lib/execution/runner.js:141:36)
#     at ensureConnectionCallback (/home/runner/work/propr/propr/node_modules/knex/lib/execution/internal/ensure-connection-callback.js:13:17)
#     at Runner.ensureConnection (/home/runner/work/propr/propr/node_modules/knex/lib/execution/runner.js:318:20)
#     at async Runner.run (/home/runner/work/propr/propr/node_modules/knex/lib/execution/runner.js:30:19)
#     at async getConfigWithClient (/home/runner/work/propr/propr/packages/core/src/config/configStore.ts:17:24)
#     at async loadSummarizationRuntimeState (/home/runner/work/propr/propr/packages/core/src/config/configManagerSummarization.ts:101:19) {
#   code: 'SQLITE_ERROR'
# }
# Error loading summarization warnings: SqliteError: select * from `system_configs` where `key` = 'summarization_runtime_state' limit 1 - no such table: system_configs
#     at Database.prepare (/home/runner/work/propr/propr/node_modules/better-sqlite3/lib/methods/wrappers.js:5:21)
#     at Client_BetterSQLite3._query (/home/runner/work/propr/propr/node_modules/knex/lib/dialects/better-sqlite3/index.js:35:34)
#     at executeQuery (/home/runner/work/propr/propr/node_modules/knex/lib/execution/internal/query-executioner.js:37:17)
#     at Client_BetterSQLite3.query (/home/runner/work/propr/propr/node_modules/knex/lib/client.js:154:12)
#     at Runner.query (/home/runner/work/propr/propr/node_modules/knex/lib/execution/runner.js:141:36)
#     at ensureConnectionCallback (/home/runner/work/propr/propr/node_modules/knex/lib/execution/internal/ensure-connection-callback.js:13:17)
#     at Runner.ensureConnection (/home/runner/work/propr/propr/node_modules/knex/lib/execution/runner.js:318:20)
#     at async Runner.run (/home/runner/work/propr/propr/node_modules/knex/lib/execution/runner.js:30:19)
#     at async getConfigWithClient (/home/runner/work/propr/propr/packages/core/src/config/configStore.ts:17:24)
#     at async loadSummarizationRuntimeState (/home/runner/work/propr/propr/packages/core/src/config/configManagerSummarization.ts:101:19) {
#   code: 'SQLITE_ERROR'
# }
# Subtest: /api/status caches agent health checks briefly
ok 57 - /api/status caches agent health checks briefly
  ---
  duration_ms: 9.948914
  type: 'test'
  ...
# Error loading summarization warnings: SqliteError: select * from `system_configs` where `key` = 'summarization_runtime_state' limit 1 - no such table: system_configs
#     at Database.prepare (/home/runner/work/propr/propr/node_modules/better-sqlite3/lib/methods/wrappers.js:5:21)
#     at Client_BetterSQLite3._query (/home/runner/work/propr/propr/node_modules/knex/lib/dialects/better-sqlite3/index.js:35:34)
#     at executeQuery (/home/runner/work/propr/propr/node_modules/knex/lib/execution/internal/query-executioner.js:37:17)
#     at Client_BetterSQLite3.query (/home/runner/work/propr/propr/node_modules/knex/lib/client.js:154:12)
#     at Runner.query (/home/runner/work/propr/propr/node_modules/knex/lib/execution/runner.js:141:36)
#     at ensureConnectionCallback (/home/runner/work/propr/propr/node_modules/knex/lib/execution/internal/ensure-connection-callback.js:13:17)
#     at Runner.ensureConnection (/home/runner/work/propr/propr/node_modules/knex/lib/execution/runner.js:318:20)
#     at async Runner.run (/home/runner/work/propr/propr/node_modules/knex/lib/execution/runner.js:30:19)
#     at async getConfigWithClient (/home/runner/work/propr/propr/packages/core/src/config/configStore.ts:17:24)
#     at async loadSummarizationRuntimeState (/home/runner/work/propr/propr/packages/core/src/config/configManagerSummarization.ts:101:19) {
#   code: 'SQLITE_ERROR'
# }
# Error loading summarization warnings: SqliteError: select * from `system_configs` where `key` = 'summarization_runtime_state' limit 1 - no such table: system_configs
#     at Database.prepare (/home/runner/work/propr/propr/node_modules/better-sqlite3/lib/methods/wrappers.js:5:21)
#     at Client_BetterSQLite3._query (/home/runner/work/propr/propr/node_modules/knex/lib/dialects/better-sqlite3/index.js:35:34)
#     at executeQuery (/home/runner/work/propr/propr/node_modules/knex/lib/execution/internal/query-executioner.js:37:17)
#     at Client_BetterSQLite3.query (/home/runner/work/propr/propr/node_modules/knex/lib/client.js:154:12)
#     at Runner.query (/home/runner/work/propr/propr/node_modules/knex/lib/execution/runner.js:141:36)
#     at ensureConnectionCallback (/home/runner/work/propr/propr/node_modules/knex/lib/execution/internal/ensure-connection-callback.js:13:17)
#     at Runner.ensureConnection (/home/runner/work/propr/propr/node_modules/knex/lib/execution/runner.js:318:20)
#     at async Runner.run (/home/runner/work/propr/propr/node_modules/knex/lib/execution/runner.js:30:19)
#     at async getConfigWithClient (/home/runner/work/propr/propr/packages/core/src/config/configStore.ts:17:24)
#     at async loadSummarizationRuntimeState (/home/runner/work/propr/propr/packages/core/src/config/configManagerSummarization.ts:101:19) {
#   code: 'SQLITE_ERROR'
# }
# Subtest: /api/status marks an unavailable synthetic pool degraded without downgrading direct agents
ok 58 - /api/status marks an unavailable synthetic pool degraded without downgrading direct agents
  ---
  duration_ms: 3.321492
  type: 'test'
  ...
# Subtest: /api/status reports resolved auth mode and event intake mode
ok 59 - /api/status reports resolved auth mode and event intake mode
  ---
  duration_ms: 2.663735
  type: 'test'
  ...
# Error loading summarization warnings: SqliteError: select * from `system_configs` where `key` = 'summarization_runtime_state' limit 1 - no such table: system_configs
#     at Database.prepare (/home/runner/work/propr/propr/node_modules/better-sqlite3/lib/methods/wrappers.js:5:21)
#     at Client_BetterSQLite3._query (/home/runner/work/propr/propr/node_modules/knex/lib/dialects/better-sqlite3/index.js:35:34)
#     at executeQuery (/home/runner/work/propr/propr/node_modules/knex/lib/execution/internal/query-executioner.js:37:17)
#     at Client_BetterSQLite3.query (/home/runner/work/propr/propr/node_modules/knex/lib/client.js:154:12)
#     at Runner.query (/home/runner/work/propr/propr/node_modules/knex/lib/execution/runner.js:141:36)
#     at ensureConnectionCallback (/home/runner/work/propr/propr/node_modules/knex/lib/execution/internal/ensure-connection-callback.js:13:17)
#     at Runner.ensureConnection (/home/runner/work/propr/propr/node_modules/knex/lib/execution/runner.js:318:20)
#     at async Runner.run (/home/runner/work/propr/propr/node_modules/knex/lib/execution/runner.js:30:19)
#     at async getConfigWithClient (/home/runner/work/propr/propr/packages/core/src/config/configStore.ts:17:24)
#     at async loadSummarizationRuntimeState (/home/runner/work/propr/propr/packages/core/src/config/configManagerSummarization.ts:101:19) {
#   code: 'SQLITE_ERROR'
# }
# Subtest: /api/status reports connected intake status when routing state is live
ok 60 - /api/status reports connected intake status when routing state is live
  ---
  duration_ms: 3.288063
  type: 'test'
  ...
# Error loading summarization warnings: SqliteError: select * from `system_configs` where `key` = 'summarization_runtime_state' limit 1 - no such table: system_configs
#     at Database.prepare (/home/runner/work/propr/propr/node_modules/better-sqlite3/lib/methods/wrappers.js:5:21)
#     at Client_BetterSQLite3._query (/home/runner/work/propr/propr/node_modules/knex/lib/dialects/better-sqlite3/index.js:35:34)
#     at executeQuery (/home/runner/work/propr/propr/node_modules/knex/lib/execution/internal/query-executioner.js:37:17)
#     at Client_BetterSQLite3.query (/home/runner/work/propr/propr/node_modules/knex/lib/client.js:154:12)
#     at Runner.query (/home/runner/work/propr/propr/node_modules/knex/lib/execution/runner.js:141:36)
#     at ensureConnectionCallback (/home/runner/work/propr/propr/node_modules/knex/lib/execution/internal/ensure-connection-callback.js:13:17)
#     at Runner.ensureConnection (/home/runner/work/propr/propr/node_modules/knex/lib/execution/runner.js:318:20)
#     at async Runner.run (/home/runner/work/propr/propr/node_modules/knex/lib/execution/runner.js:30:19)
#     at async getConfigWithClient (/home/runner/work/propr/propr/packages/core/src/config/configStore.ts:17:24)
#     at async loadSummarizationRuntimeState (/home/runner/work/propr/propr/packages/core/src/config/configManagerSummarization.ts:101:19) {
#   code: 'SQLITE_ERROR'
# }
# Subtest: /api/status reports disconnected intake status when routing state is down
ok 61 - /api/status reports disconnected intake status when routing state is down
  ---
  duration_ms: 3.241684
  type: 'test'
  ...
# Error loading summarization warnings: SqliteError: select * from `system_configs` where `key` = 'summarization_runtime_state' limit 1 - no such table: system_configs
#     at Database.prepare (/home/runner/work/propr/propr/node_modules/better-sqlite3/lib/methods/wrappers.js:5:21)
#     at Client_BetterSQLite3._query (/home/runner/work/propr/propr/node_modules/knex/lib/dialects/better-sqlite3/index.js:35:34)
#     at executeQuery (/home/runner/work/propr/propr/node_modules/knex/lib/execution/internal/query-executioner.js:37:17)
#     at Client_BetterSQLite3.query (/home/runner/work/propr/propr/node_modules/knex/lib/client.js:154:12)
#     at Runner.query (/home/runner/work/propr/propr/node_modules/knex/lib/execution/runner.js:141:36)
#     at ensureConnectionCallback (/home/runner/work/propr/propr/node_modules/knex/lib/execution/internal/ensure-connection-callback.js:13:17)
#     at Runner.ensureConnection (/home/runner/work/propr/propr/node_modules/knex/lib/execution/runner.js:318:20)
#     at async Runner.run (/home/runner/work/propr/propr/node_modules/knex/lib/execution/runner.js:30:19)
#     at async getConfigWithClient (/home/runner/work/propr/propr/packages/core/src/config/configStore.ts:17:24)
#     at async loadSummarizationRuntimeState (/home/runner/work/propr/propr/packages/core/src/config/configManagerSummarization.ts:101:19) {
#   code: 'SQLITE_ERROR'
# }
# Subtest: /api/status reports active intake status for polling when the daemon is running
ok 62 - /api/status reports active intake status for polling when the daemon is running
  ---
  duration_ms: 1.813784
  type: 'test'
  ...
# Error loading summarization warnings: SqliteError: select * from `system_configs` where `key` = 'summarization_runtime_state' limit 1 - no such table: system_configs
#     at Database.prepare (/home/runner/work/propr/propr/node_modules/better-sqlite3/lib/methods/wrappers.js:5:21)
#     at Client_BetterSQLite3._query (/home/runner/work/propr/propr/node_modules/knex/lib/dialects/better-sqlite3/index.js:35:34)
#     at executeQuery (/home/runner/work/propr/propr/node_modules/knex/lib/execution/internal/query-executioner.js:37:17)
#     at Client_BetterSQLite3.query (/home/runner/work/propr/propr/node_modules/knex/lib/client.js:154:12)
#     at Runner.query (/home/runner/work/propr/propr/node_modules/knex/lib/execution/runner.js:141:36)
#     at ensureConnectionCallback (/home/runner/work/propr/propr/node_modules/knex/lib/execution/internal/ensure-connection-callback.js:13:17)
#     at Runner.ensureConnection (/home/runner/work/propr/propr/node_modules/knex/lib/execution/runner.js:318:20)
#     at async Runner.run (/home/runner/work/propr/propr/node_modules/knex/lib/execution/runner.js:30:19)
#     at async getConfigWithClient (/home/runner/work/propr/propr/packages/core/src/config/configStore.ts:17:24)
#     at async loadSummarizationRuntimeState (/home/runner/work/propr/propr/packages/core/src/config/configManagerSummarization.ts:101:19) {
#   code: 'SQLITE_ERROR'
# }
# Subtest: /api/status reports disconnected intake status for polling when the daemon is stopped
ok 63 - /api/status reports disconnected intake status for polling when the daemon is stopped
  ---
  duration_ms: 1.813193
  type: 'test'
  ...
# Error loading summarization warnings: SqliteError: select * from `system_configs` where `key` = 'summarization_runtime_state' limit 1 - no such table: system_configs
#     at Database.prepare (/home/runner/work/propr/propr/node_modules/better-sqlite3/lib/methods/wrappers.js:5:21)
#     at Client_BetterSQLite3._query (/home/runner/work/propr/propr/node_modules/knex/lib/dialects/better-sqlite3/index.js:35:34)
#     at executeQuery (/home/runner/work/propr/propr/node_modules/knex/lib/execution/internal/query-executioner.js:37:17)
#     at Client_BetterSQLite3.query (/home/runner/work/propr/propr/node_modules/knex/lib/client.js:154:12)
#     at Runner.query (/home/runner/work/propr/propr/node_modules/knex/lib/execution/runner.js:141:36)
#     at ensureConnectionCallback (/home/runner/work/propr/propr/node_modules/knex/lib/execution/internal/ensure-connection-callback.js:13:17)
#     at Runner.ensureConnection (/home/runner/work/propr/propr/node_modules/knex/lib/execution/runner.js:318:20)
#     at async Runner.run (/home/runner/work/propr/propr/node_modules/knex/lib/execution/runner.js:30:19)
#     at async getConfigWithClient (/home/runner/work/propr/propr/packages/core/src/config/configStore.ts:17:24)
#     at async loadSummarizationRuntimeState (/home/runner/work/propr/propr/packages/core/src/config/configManagerSummarization.ts:101:19) {
#   code: 'SQLITE_ERROR'
# }
# Error loading summarization warnings: SqliteError: select * from `system_configs` where `key` = 'summarization_runtime_state' limit 1 - no such table: system_configs
#     at Database.prepare (/home/runner/work/propr/propr/node_modules/better-sqlite3/lib/methods/wrappers.js:5:21)
#     at Client_BetterSQLite3._query (/home/runner/work/propr/propr/node_modules/knex/lib/dialects/better-sqlite3/index.js:35:34)
#     at executeQuery (/home/runner/work/propr/propr/node_modules/knex/lib/execution/internal/query-executioner.js:37:17)
#     at Client_BetterSQLite3.query (/home/runner/work/propr/propr/node_modules/knex/lib/client.js:154:12)
#     at Runner.query (/home/runner/work/propr/propr/node_modules/knex/lib/execution/runner.js:141:36)
#     at ensureConnectionCallback (/home/runner/work/propr/propr/node_modules/knex/lib/execution/internal/ensure-connection-callback.js:13:17)
#     at Runner.ensureConnection (/home/runner/work/propr/propr/node_modules/knex/lib/execution/runner.js:318:20)
#     at async Runner.run (/home/runner/work/propr/propr/node_modules/knex/lib/execution/runner.js:30:19)
#     at async getConfigWithClient (/home/runner/work/propr/propr/packages/core/src/config/configStore.ts:17:24)
#     at async loadSummarizationRuntimeState (/home/runner/work/propr/propr/packages/core/src/config/configManagerSummarization.ts:101:19) {
#   code: 'SQLITE_ERROR'
# }
# Error loading summarization warnings: SqliteError: select * from `system_configs` where `key` = 'summarization_runtime_state' limit 1 - no such table: system_configs
#     at Database.prepare (/home/runner/work/propr/propr/node_modules/better-sqlite3/lib/methods/wrappers.js:5:21)
#     at Client_BetterSQLite3._query (/home/runner/work/propr/propr/node_modules/knex/lib/dialects/better-sqlite3/index.js:35:34)
#     at executeQuery (/home/runner/work/propr/propr/node_modules/knex/lib/execution/internal/query-executioner.js:37:17)
#     at Client_BetterSQLite3.query (/home/runner/work/propr/propr/node_modules/knex/lib/client.js:154:12)
#     at Runner.query (/home/runner/work/propr/propr/node_modules/knex/lib/execution/runner.js:141:36)
#     at ensureConnectionCallback (/home/runner/work/propr/propr/node_modules/knex/lib/execution/internal/ensure-connection-callback.js:13:17)
#     at Runner.ensureConnection (/home/runner/work/propr/propr/node_modules/knex/lib/execution/runner.js:318:20)
#     at async Runner.run (/home/runner/work/propr/propr/node_modules/knex/lib/execution/runner.js:30:19)
#     at async getConfigWithClient (/home/runner/work/propr/propr/packages/core/src/config/configStore.ts:17:24)
#     at async loadSummarizationRuntimeState (/home/runner/work/propr/propr/packages/core/src/config/configManagerSummarization.ts:101:19) {
#   code: 'SQLITE_ERROR'
# }
# Error loading summarization warnings: SqliteError: select * from `system_configs` where `key` = 'summarization_runtime_state' limit 1 - no such table: system_configs
#     at Database.prepare (/home/runner/work/propr/propr/node_modules/better-sqlite3/lib/methods/wrappers.js:5:21)
#     at Client_BetterSQLite3._query (/home/runner/work/propr/propr/node_modules/knex/lib/dialects/better-sqlite3/index.js:35:34)
#     at executeQuery (/home/runner/work/propr/propr/node_modules/knex/lib/execution/internal/query-executioner.js:37:17)
#     at Client_BetterSQLite3.query (/home/runner/work/propr/propr/node_modules/knex/lib/client.js:154:12)
#     at Runner.query (/home/runner/work/propr/propr/node_modules/knex/lib/execution/runner.js:141:36)
#     at ensureConnectionCallback (/home/runner/work/propr/propr/node_modules/knex/lib/execution/internal/ensure-connection-callback.js:13:17)
#     at Runner.ensureConnection (/home/runner/work/propr/propr/node_modules/knex/lib/execution/runner.js:318:20)
#     at async Runner.run (/home/runner/work/propr/propr/node_modules/knex/lib/execution/runner.js:30:19)
#     at async getConfigWithClient (/home/runner/work/propr/propr/packages/core/src/config/configStore.ts:17:24)
#     at async loadSummarizationRuntimeState (/home/runner/work/propr/propr/packages/core/src/config/configManagerSummarization.ts:101:19) {
#   code: 'SQLITE_ERROR'
# }
# Subtest: /api/status includes routing state published by the daemon
ok 64 - /api/status includes routing state published by the daemon
  ---
  duration_ms: 2.223182
  type: 'test'
  ...
# Subtest: /api/status exposes only validated UI-safe Connect account fields
ok 65 - /api/status exposes only validated UI-safe Connect account fields
  ---
  duration_ms: 2.144275
  type: 'test'
  ...
# Error loading summarization warnings: SqliteError: select * from `system_configs` where `key` = 'summarization_runtime_state' limit 1 - no such table: system_configs
#     at Database.prepare (/home/runner/work/propr/propr/node_modules/better-sqlite3/lib/methods/wrappers.js:5:21)
#     at Client_BetterSQLite3._query (/home/runner/work/propr/propr/node_modules/knex/lib/dialects/better-sqlite3/index.js:35:34)
#     at executeQuery (/home/runner/work/propr/propr/node_modules/knex/lib/execution/internal/query-executioner.js:37:17)
#     at Client_BetterSQLite3.query (/home/runner/work/propr/propr/node_modules/knex/lib/client.js:154:12)
#     at Runner.query (/home/runner/work/propr/propr/node_modules/knex/lib/execution/runner.js:141:36)
#     at ensureConnectionCallback (/home/runner/work/propr/propr/node_modules/knex/lib/execution/internal/ensure-connection-callback.js:13:17)
#     at Runner.ensureConnection (/home/runner/work/propr/propr/node_modules/knex/lib/execution/runner.js:318:20)
#     at async Runner.run (/home/runner/work/propr/propr/node_modules/knex/lib/execution/runner.js:30:19)
#     at async getConfigWithClient (/home/runner/work/propr/propr/packages/core/src/config/configStore.ts:17:24)
#     at async loadSummarizationRuntimeState (/home/runner/work/propr/propr/packages/core/src/config/configManagerSummarization.ts:101:19) {
#   code: 'SQLITE_ERROR'
# }
# Error loading summarization warnings: SqliteError: select * from `system_configs` where `key` = 'summarization_runtime_state' limit 1 - no such table: system_configs
#     at Database.prepare (/home/runner/work/propr/propr/node_modules/better-sqlite3/lib/methods/wrappers.js:5:21)
#     at Client_BetterSQLite3._query (/home/runner/work/propr/propr/node_modules/knex/lib/dialects/better-sqlite3/index.js:35:34)
#     at executeQuery (/home/runner/work/propr/propr/node_modules/knex/lib/execution/internal/query-executioner.js:37:17)
#     at Client_BetterSQLite3.query (/home/runner/work/propr/propr/node_modules/knex/lib/client.js:154:12)
#     at Runner.query (/home/runner/work/propr/propr/node_modules/knex/lib/execution/runner.js:141:36)
#     at ensureConnectionCallback (/home/runner/work/propr/propr/node_modules/knex/lib/execution/internal/ensure-connection-callback.js:13:17)
#     at Runner.ensureConnection (/home/runner/work/propr/propr/node_modules/knex/lib/execution/runner.js:318:20)
#     at async Runner.run (/home/runner/work/propr/propr/node_modules/knex/lib/execution/runner.js:30:19)
#     at async getConfigWithClient (/home/runner/work/propr/propr/packages/core/src/config/configStore.ts:17:24)
#     at async loadSummarizationRuntimeState (/home/runner/work/propr/propr/packages/core/src/config/configManagerSummarization.ts:101:19) {
#   code: 'SQLITE_ERROR'
# }
# Error loading summarization warnings: SqliteError: select * from `system_configs` where `key` = 'summarization_runtime_state' limit 1 - no such table: system_configs
#     at Database.prepare (/home/runner/work/propr/propr/node_modules/better-sqlite3/lib/methods/wrappers.js:5:21)
#     at Client_BetterSQLite3._query (/home/runner/work/propr/propr/node_modules/knex/lib/dialects/better-sqlite3/index.js:35:34)
#     at executeQuery (/home/runner/work/propr/propr/node_modules/knex/lib/execution/internal/query-executioner.js:37:17)
#     at Client_BetterSQLite3.query (/home/runner/work/propr/propr/node_modules/knex/lib/client.js:154:12)
#     at Runner.query (/home/runner/work/propr/propr/node_modules/knex/lib/execution/runner.js:141:36)
#     at ensureConnectionCallback (/home/runner/work/propr/propr/node_modules/knex/lib/execution/internal/ensure-connection-callback.js:13:17)
#     at Runner.ensureConnection (/home/runner/work/propr/propr/node_modules/knex/lib/execution/runner.js:318:20)
#     at async Runner.run (/home/runner/work/propr/propr/node_modules/knex/lib/execution/runner.js:30:19)
#     at async getConfigWithClient (/home/runner/work/propr/propr/packages/core/src/config/configStore.ts:17:24)
#     at async loadSummarizationRuntimeState (/home/runner/work/propr/propr/packages/core/src/config/configManagerSummarization.ts:101:19) {
#   code: 'SQLITE_ERROR'
# }
# Subtest: /api/status rejects impossible account dates and preserves valid leap-day instants
ok 66 - /api/status rejects impossible account dates and preserves valid leap-day instants
  ---
  duration_ms: 6.112226
  type: 'test'
  ...
# Error loading summarization warnings: SqliteError: select * from `system_configs` where `key` = 'summarization_runtime_state' limit 1 - no such table: system_configs
#     at Database.prepare (/home/runner/work/propr/propr/node_modules/better-sqlite3/lib/methods/wrappers.js:5:21)
#     at Client_BetterSQLite3._query (/home/runner/work/propr/propr/node_modules/knex/lib/dialects/better-sqlite3/index.js:35:34)
#     at executeQuery (/home/runner/work/propr/propr/node_modules/knex/lib/execution/internal/query-executioner.js:37:17)
#     at Client_BetterSQLite3.query (/home/runner/work/propr/propr/node_modules/knex/lib/client.js:154:12)
#     at Runner.query (/home/runner/work/propr/propr/node_modules/knex/lib/execution/runner.js:141:36)
#     at ensureConnectionCallback (/home/runner/work/propr/propr/node_modules/knex/lib/execution/internal/ensure-connection-callback.js:13:17)
#     at Runner.ensureConnection (/home/runner/work/propr/propr/node_modules/knex/lib/execution/runner.js:318:20)
#     at async Runner.run (/home/runner/work/propr/propr/node_modules/knex/lib/execution/runner.js:30:19)
#     at async getConfigWithClient (/home/runner/work/propr/propr/packages/core/src/config/configStore.ts:17:24)
#     at async loadSummarizationRuntimeState (/home/runner/work/propr/propr/packages/core/src/config/configManagerSummarization.ts:101:19) {
#   code: 'SQLITE_ERROR'
# }
# Error loading summarization warnings: SqliteError: select * from `system_configs` where `key` = 'summarization_runtime_state' limit 1 - no such table: system_configs
#     at Database.prepare (/home/runner/work/propr/propr/node_modules/better-sqlite3/lib/methods/wrappers.js:5:21)
#     at Client_BetterSQLite3._query (/home/runner/work/propr/propr/node_modules/knex/lib/dialects/better-sqlite3/index.js:35:34)
#     at executeQuery (/home/runner/work/propr/propr/node_modules/knex/lib/execution/internal/query-executioner.js:37:17)
#     at Client_BetterSQLite3.query (/home/runner/work/propr/propr/node_modules/knex/lib/client.js:154:12)
#     at Runner.query (/home/runner/work/propr/propr/node_modules/knex/lib/execution/runner.js:141:36)
#     at ensureConnectionCallback (/home/runner/work/propr/propr/node_modules/knex/lib/execution/internal/ensure-connection-callback.js:13:17)
#     at Runner.ensureConnection (/home/runner/work/propr/propr/node_modules/knex/lib/execution/runner.js:318:20)
#     at async Runner.run (/home/runner/work/propr/propr/node_modules/knex/lib/execution/runner.js:30:19)
#     at async getConfigWithClient (/home/runner/work/propr/propr/packages/core/src/config/configStore.ts:17:24)
#     at async loadSummarizationRuntimeState (/home/runner/work/propr/propr/packages/core/src/config/configManagerSummarization.ts:101:19) {
#   code: 'SQLITE_ERROR'
# }
# Subtest: /api/status drops malformed or disconnected Connect account state without assuming Community
ok 67 - /api/status drops malformed or disconnected Connect account state without assuming Community
  ---
  duration_ms: 3.083528
  type: 'test'
  ...
# Subtest: /api/status does not expose Connect account state for a non-Connect intake mode
ok 68 - /api/status does not expose Connect account state for a non-Connect intake mode
  ---
  duration_ms: 1.607297
  type: 'test'
  ...
# Error loading summarization warnings: SqliteError: select * from `system_configs` where `key` = 'summarization_runtime_state' limit 1 - no such table: system_configs
#     at Database.prepare (/home/runner/work/propr/propr/node_modules/better-sqlite3/lib/methods/wrappers.js:5:21)
#     at Client_BetterSQLite3._query (/home/runner/work/propr/propr/node_modules/knex/lib/dialects/better-sqlite3/index.js:35:34)
#     at executeQuery (/home/runner/work/propr/propr/node_modules/knex/lib/execution/internal/query-executioner.js:37:17)
#     at Client_BetterSQLite3.query (/home/runner/work/propr/propr/node_modules/knex/lib/client.js:154:12)
#     at Runner.query (/home/runner/work/propr/propr/node_modules/knex/lib/execution/runner.js:141:36)
#     at ensureConnectionCallback (/home/runner/work/propr/propr/node_modules/knex/lib/execution/internal/ensure-connection-callback.js:13:17)
#     at Runner.ensureConnection (/home/runner/work/propr/propr/node_modules/knex/lib/execution/runner.js:318:20)
#     at async Runner.run (/home/runner/work/propr/propr/node_modules/knex/lib/execution/runner.js:30:19)
#     at async getConfigWithClient (/home/runner/work/propr/propr/packages/core/src/config/configStore.ts:17:24)
#     at async loadSummarizationRuntimeState (/home/runner/work/propr/propr/packages/core/src/config/configManagerSummarization.ts:101:19) {
#   code: 'SQLITE_ERROR'
# }
# Subtest: /api/status reports connected githubAuth for relay-auth deployments
ok 69 - /api/status reports connected githubAuth for relay-auth deployments
  ---
  duration_ms: 2.26263
  type: 'test'
  ...
# Error loading summarization warnings: SqliteError: select * from `system_configs` where `key` = 'summarization_runtime_state' limit 1 - no such table: system_configs
#     at Database.prepare (/home/runner/work/propr/propr/node_modules/better-sqlite3/lib/methods/wrappers.js:5:21)
#     at Client_BetterSQLite3._query (/home/runner/work/propr/propr/node_modules/knex/lib/dialects/better-sqlite3/index.js:35:34)
#     at executeQuery (/home/runner/work/propr/propr/node_modules/knex/lib/execution/internal/query-executioner.js:37:17)
#     at Client_BetterSQLite3.query (/home/runner/work/propr/propr/node_modules/knex/lib/client.js:154:12)
#     at Runner.query (/home/runner/work/propr/propr/node_modules/knex/lib/execution/runner.js:141:36)
#     at ensureConnectionCallback (/home/runner/work/propr/propr/node_modules/knex/lib/execution/internal/ensure-connection-callback.js:13:17)
#     at Runner.ensureConnection (/home/runner/work/propr/propr/node_modules/knex/lib/execution/runner.js:318:20)
#     at async Runner.run (/home/runner/work/propr/propr/node_modules/knex/lib/execution/runner.js:30:19)
#     at async getConfigWithClient (/home/runner/work/propr/propr/packages/core/src/config/configStore.ts:17:24)
#     at async loadSummarizationRuntimeState (/home/runner/work/propr/propr/packages/core/src/config/configManagerSummarization.ts:101:19) {
#   code: 'SQLITE_ERROR'
# }
# Error loading summarization warnings: SqliteError: select * from `system_configs` where `key` = 'summarization_runtime_state' limit 1 - no such table: system_configs
#     at Database.prepare (/home/runner/work/propr/propr/node_modules/better-sqlite3/lib/methods/wrappers.js:5:21)
#     at Client_BetterSQLite3._query (/home/runner/work/propr/propr/node_modules/knex/lib/dialects/better-sqlite3/index.js:35:34)
#     at executeQuery (/home/runner/work/propr/propr/node_modules/knex/lib/execution/internal/query-executioner.js:37:17)
#     at Client_BetterSQLite3.query (/home/runner/work/propr/propr/node_modules/knex/lib/client.js:154:12)
#     at Runner.query (/home/runner/work/propr/propr/node_modules/knex/lib/execution/runner.js:141:36)
#     at ensureConnectionCallback (/home/runner/work/propr/propr/node_modules/knex/lib/execution/internal/ensure-connection-callback.js:13:17)
#     at Runner.ensureConnection (/home/runner/work/propr/propr/node_modules/knex/lib/execution/runner.js:318:20)
#     at async Runner.run (/home/runner/work/propr/propr/node_modules/knex/lib/execution/runner.js:30:19)
#     at async getConfigWithClient (/home/runner/work/propr/propr/packages/core/src/config/configStore.ts:17:24)
#     at async loadSummarizationRuntimeState (/home/runner/work/propr/propr/packages/core/src/config/configManagerSummarization.ts:101:19) {
#   code: 'SQLITE_ERROR'
# }
# Subte

...(truncated)

View Workflow

@integry

integry commented Sep 6, 2026

Copy link
Copy Markdown
Owner

Additional exact CI evidence for the same pending correction pass: Validate Changes run34026886517/job101469336395 fails only UI lint here: propr-ui/src/desktop/electronAdapters.test.ts:445 max-lines warning (406 versus limit400), with --max-warnings0. Root inspected the complete build-check-output artifact; UI typecheck and CLI lint pass. Split the new setup-specific tests into a focused file without disabling lint or dropping coverage. This is a small build correction, not a new feature.

@propr-dev

propr-dev Bot commented Sep 6, 2026

Copy link
Copy Markdown
Contributor Author

🔍 AI Code Review — codex:gpt-5.6-sol

Overall Evaluation

This PR needs significant rework before merge. The architecture is thoughtful, but the real setup path currently breaks synchronous host-action contracts, has an admission/cancellation race, and mishandles failures after persisted-secret reconfiguration.

Narrow privilege boundary — Renderer requests are strictly parsed, while private keys and webhook secrets remain behind opaque, session-bound main-process capabilities.

Defensive filesystem validation — Setup roots and selected key files receive ownership, permission, symlink, inode, and boundary checks.

Strict connection handoff — Successful setup is routed through the existing probe, pairing, activation, and profile flow, with focused integration coverage.

Merge blockers

Every finding below was introduced by this PR and must be resolved before merging.

F1: 🔴 Proxy breaks synchronous actions

  • Required behavior: Wrapping setup actions must preserve the SetupActions return contracts; synchronous actions must remain synchronous for the setup engine to consume them correctly.
  • Evidence: apps/desktop/src/setup-capabilities.ts:77 — bindRootOperations wraps every function with Promise.resolve(result).then(...), so synchronous methods such as inspectStackInit, readEnvVars, detectGithubAuthMode, and hasGithubToken return promises instead of their declared values.
  • Minimum fix: Preserve synchronous returns by validating and returning non-thenable results directly; only attach asynchronous post-validation to actual promises. Add a happy-path regression test containing both synchronous and asynchronous actions.

F2: 🔴 Setup admission is not atomic

  • Required behavior: Setup must be single-flight, and cancellation or shutdown must cover all admitted setup work, including capability resolution.
  • Evidence: apps/desktop/src/setup-controller.ts:115 — #begin checks #current, then awaits private-key consumption before #runResolved creates the abort controller and assigns #current. Another start can therefore be admitted during that await, while cancellation or shutdown sees no operation to abort or await.
  • Minimum fix: Acquire an admission guard and establish the abort controller before any awaited preflight work, pass its signal into capability consumption, and track the entire preflight-plus-run promise through cancellation and shutdown.

F3: 🔴 Reconfigured failures bypass recovery

  • Required behavior: Persisted setups requiring secret re-entry must return to the normal failure and retry flow if the reconfigured attempt fails or is cancelled.
  • Evidence: propr-ui/src/desktop/LocalSetupWizard.tsx:105 — reconfiguring is set to true when reviewing saved choices but is never cleared after adapter.retry(request) returns; the terminal recovery branch at line 118 explicitly requires !reconfiguring.
  • Minimum fix: Clear reconfiguring after a submitted retry returns a failed or cancelled snapshot so the recovery view displays the failure and can retry using the controller’s resolved credentials. Retain form mode only when submission itself rejects due to values requiring correction.

Suggestions

These are optional follow-ups and are not sent to /fix.

S1: 🟢 Interrupt long subprocesses

The new signal checks in packages/cli/src/commands/setup/hostActions.ts occur around dockerAsync and stack-start calls but do not interrupt an operation already in progress. Propagating cancellation into the child-process runner would keep “Cancel safely” responsive during slow or hung Docker commands. This is optional because the documented engine contract is cooperative at action boundaries.

S2: 🟢 Strengthen resource integrity

apps/desktop/forge.config.ts places executable orchestrator.mjs outside the ASAR as an extra resource, while packages/cli/src/desktopLocalSetup.ts validates only its file type and path. If installation-directory immutability is not guaranteed for every Linux distribution format, consider verifying a pinned digest or keeping executable setup code under ASAR integrity protection. This is defense in depth rather than a demonstrated functional failure.

Score

The security-oriented design is promising, and no current check has failed, but the synchronous-action regression prevents confidence in the primary setup journey while two lifecycle/recovery defects remain.

Score: 4/10


🤖 Review Details

  • Model: GPT-5.6 Sol
  • Time: 5m 39s
  • Tokens: 81,943 (67,868 in / 14,075 out)
  • Cost: $0.76

View Task


💡 Next step: Comment /fix to address F# merge blockers only.
F# IDs increment across review comments and remain permanent, so selectors such as /fix F3 F5 stay unambiguous across cycles. Suggestions require a separate ordinary follow-up request.

🤖 Review by ProPR

@propr-dev

propr-dev Bot commented Sep 6, 2026

Copy link
Copy Markdown
Contributor Author

Applied the requested follow-up changes in commit a54f16b

Processed 3 comments:

  • Comment 1 by @integry (ID: 5558574637✓)
  • Comment 2 by @github-actions[bot] (ID: 5558575451✓)
  • Comment 3 by @integry (ID: 5558580024✓)

Summary of Changes

Implemented the PR #2132 follow-up corrections.

  • Desktop GitHub and agent authentication now opens in a visible Linux terminal asynchronously and supports cancellation/process-tree termination.
  • macOS temp fixtures use canonical native paths while production symlink protections remain unchanged.
  • Setup status, cancellation, and recovery retry failures are visible with working Back/Retry actions.
  • Setup adapter tests were split into a focused file, resolving the UI max-lines lint failure.
  • Added host-boundary tests without credentials, Docker installation, or a real stack.
  • Generated focused transient preview evidence for the recovery screen.

Verification passed:

  • 80 focused setup/host/engine tests
  • 21 focused UI tests
  • Full desktop suite: 497 passed, 25 platform skips
  • CLI, desktop, and UI typechecks
  • CLI lint and UI lint with --max-warnings 0
  • git diff --check

No commit, PR, workflow, or integration action was performed.

Visual preview

Desktop setup status recovery

Desktop setup status recovery

The guided Linux setup now surfaces an unavailable status with visible Back and Retry actions at a 1280×820 desktop viewport.


🤖 Implementation Details

  • Model: GPT-5.6 Sol (high)
  • Requested By: @integry, @github-actions[bot]
  • Time: 17m 14s
  • Tokens: 11,319,983 (11,290,635 in / 29,348 out)
  • Cost: $7.49
  • Subscription usage: Weekly +2%

Undo ChangesView Task Execution


💡 ProPR Slash Commands
Command Description Example
/merge Merge target branch into this PR and resolve conflicts /merge
/review Request an AI code review /review or /review claude-sonnet
/fix Implement fixes for issues found by /review /fix or /fix address the null check issue
/switch Change the AI model for this PR /switch claude-opus
/use Override the model for a single follow-up run /use claude-sonnet
/ultrafix Loop review→fix cycles until score goal is met /ultrafix or /ultrafix goal=8 max=10

🤖 Generated by ProPR
Processing comment IDs: 5558574637✓, 5558575451✓, 5558580024✓

@integry

integry commented Sep 6, 2026

Copy link
Copy Markdown
Owner

/fix F1 F2 F3

Implemented the PR #2132 follow-up corrections.

- Desktop GitHub and agent authentication now opens in a visible Linux terminal asynchronously and supports cancellation/process-tree termination.
- macOS temp fixtures use canonical native paths while production symlink protections remain unchanged.
- Setup status, cancellation, and recovery retry failures are visible with working Back/Retry actions.
- Setup adapter tests were split into a focused file, resolving the UI max-lines lint failure.
- Added host-boundary tests without credentials, Docker installation, or a real stack.
- Generated focused transient preview evidence for the recovery screen.

Verification passed:

- 80 focused setup/host/engine tests
- 21 focused UI tests
- Full desktop suite: 497 passed, 25 platform skips
- CLI, desktop, and UI typechecks
- CLI lint and UI lint with `--max-warnings 0`
- `git diff --check`

No commit, PR, workflow, or integration action was performed.

PR: #2132
Comment by: @integry (ID: 5558574637)
Comment by: @github-actions[bot] (ID: 5558575451)
Comment by: @integry (ID: 5558580024)
Model: gpt-5.6-sol
@propr-dev

propr-dev Bot commented Sep 6, 2026

Copy link
Copy Markdown
Contributor Author

Applied the requested follow-up changes in commit 9233372

Addressed 1 AI review comment (IDs: 5558599508)

Summary of Changes

Implemented only F1–F3:

  • Preserved synchronous SetupActions return values while validating promises asynchronously.
  • Made setup admission atomic before capability resolution; cancellation/shutdown now track and abort preflight work.
  • Restored recovery after failed or cancelled reconfigured retries while retaining form mode for rejected submissions.
  • Added focused desktop and UI regressions.

Verification:

  • Desktop suite: 499 passed, 25 skipped, 0 failed.
  • UI setup tests: 7 passed.
  • Desktop/UI typechecks passed.
  • UI lint passed with zero warnings.
  • git diff --check passed.

Visual preview

Suggested agent tools

  • Playwright Chromium: Install the Playwright Chromium browser binary in the agent image to capture the reconfigured setup failure recovery state at the production desktop viewport.

🤖 Implementation Details

  • Model: GPT-5.6 Sol (high)
  • Requested By: @integry
  • Time: 10m 25s
  • Tokens: 1,566,588 (1,548,991 in / 17,597 out)
  • Cost: $1.91
  • Subscription usage: Weekly +2%

Undo ChangesView Task Execution


💡 ProPR Slash Commands
Command Description Example
/merge Merge target branch into this PR and resolve conflicts /merge
/review Request an AI code review /review or /review claude-sonnet
/fix Implement fixes for issues found by /review /fix or /fix address the null check issue
/switch Change the AI model for this PR /switch claude-opus
/use Override the model for a single follow-up run /use claude-sonnet
/ultrafix Loop review→fix cycles until score goal is met /ultrafix or /ultrafix goal=8 max=10

🤖 Generated by ProPR
Processing comment ID: 5558605912✓

Implemented only F1–F3:

- Preserved synchronous `SetupActions` return values while validating promises asynchronously.
- Made setup admission atomic before capability resolution; cancellation/shutdown now track and abort preflight work.
- Restored recovery after failed or cancelled reconfigured retries while retaining form mode for rejected submissions.
- Added focused desktop and UI regressions.

Verification:

- Desktop suite: 499 passed, 25 skipped, 0 failed.
- UI setup tests: 7 passed.
- Desktop/UI typechecks passed.
- UI lint passed with zero warnings.
- `git diff --check` passed.

Visual capture was blocked by the missing Playwright Chromium binary; `.propr/previews/manifest.json` records the required tooling. No commit was created.

PR: #2132
Comment by: @integry (ID: 5558605912)
Model: gpt-5.6-sol
@integry

integry commented Sep 6, 2026

Copy link
Copy Markdown
Owner

/review

@propr-dev

propr-dev Bot commented Sep 6, 2026

Copy link
Copy Markdown
Contributor Author

AI Code Review Complete requested by @integry

Posted 1 review:

View Task Details

@propr-dev

propr-dev Bot commented Sep 6, 2026

Copy link
Copy Markdown
Contributor Author

🔍 AI Code Review — codex:gpt-5.6-sol

Overall Evaluation

This PR substantially restores the Linux desktop setup journey with a well-separated renderer/main-process design, but it is not ready to merge. Four correctness and safety gaps remain around production cancellation, private-key validation, credential recovery, and restart recovery; these require significant correction despite the currently reported zero failed checks. The 19 pending checks must also complete successfully.

Narrow desktop boundary — The renderer receives fixed setup operations and opaque capabilities, while setup-schema.ts validates the complete request before host actions run.

Visible authentication handoff — GitHub and agent authentication now flow through an asynchronous desktop terminal handoff with propagated abort signals instead of inherited Electron stdio.

Focused integration coverage — The PR tests IPC forwarding, cancellation/retry, unauthenticated host actions, setup-to-pairing handoff, and credential-free persisted recovery paths.

Merge blockers

Every finding below was introduced by this PR and must be resolved before merging.

F4: 🔴 Production operations do not cancel promptly

  • Required behavior: The newly exposed guided setup must provide effective cancellation and shutdown draining; cancellation cannot wait indefinitely for long-running Docker or connectivity operations to finish naturally.
  • Evidence: packages/cli/src/commands/setup/hostActions.ts:76 — pullImages accepts an abort signal but awaits orch.dockerAsync(["pull", tag]) without forwarding it; the same check-only-after-completion pattern appears in startStack around line 114. packages/local-setup/src/agents.ts:208 similarly awaits validateAgents without a signal.
  • Minimum fix: Propagate cancellation into the underlying Docker child processes and long-running connectivity work, terminate and reap those processes on abort, and add a production-host-boundary test proving that cancellation settles without waiting for the command’s natural exit.

F5: 🔴 Key size validation has a TOCTOU gap

  • Required behavior: The new opaque private-key capability must preserve its file validation guarantees through consumption and must not permit an unbounded synchronous read in Electron’s main process.
  • Evidence: apps/desktop/src/setup-capabilities.ts:119 — consume() rechecks identity and permissions but not the current file size; around line 130 it then calls readFileSync(source) without a bound. A file that was at most 1 MiB when selected can grow on the same inode before consumption.
  • Minimum fix: After opening the source, validate its owner, type, link count, permissions, and nonzero size no greater than MAX_KEY_BYTES using the opened descriptor, then perform a bounded read that rejects growth beyond that limit.

F6: 🔴 Invalid credentials trap recovery in retry

  • Required behavior: Failed setup must provide a usable recovery path when a selected GitHub App key or webhook secret needs correction.
  • Evidence: apps/desktop/src/setup-controller.ts:173 — #executeResolved() includes the resumable choices but omits reconfigurationRequired; propr-ui/src/desktop/LocalSetupWizard.tsx:125 and propr-ui/src/desktop/LocalSetupWizard.tsx:143 only reopen the form when that omitted flag is true. Otherwise retry() reuses #resolved, including the same failing credentials.
  • Minimum fix: Make credential-bearing failures expose a review/reconfiguration action during the current process—either populate the flag consistently from resume.reconfigurationStage or provide a separate review action—while retaining ordinary retry for transient failures.

F7: 🔴 Completion is lost across restart

  • Required behavior: A successfully completed setup must remain recoverable until it has been handed into the existing profile, probe, and pairing flow.
  • Evidence: apps/desktop/src/setup-controller.ts:23 — PersistedSetup stores only the phase and resume choices, not the completed profile; around line 255, #load() restores phase: "completed" without reconstructing a profile. propr-ui/src/desktop/LocalSetupWizard.tsx:140 renders completion only when both the completed phase and profile exist, so the restored state falls back to the initial form.
  • Minimum fix: Persist and validate the non-secret completed profile, or durably persist enough information to reconstruct it with a stable ID and API URL. If restoration fails, expose an explicit interrupted recovery state instead of presenting a fresh installation form.

Suggestions

These are optional follow-ups and are not sent to /fix.

S1: 🟢 Enforce mid-operation root assertions

bindRootOperations() injects assertRootAuthority into several action arguments, but the changed host actions do not consume it before their Docker handoffs. Calling it immediately before path-sensitive subprocess work would strengthen the existing same-user/TOCTOU defense. This is optional because the current fixed root is already beneath an owner-only application-data boundary.

S2: 🟢 Align numeric form validation

LocalSetupWizard.tsx accepts zero and leading-zero App or installation IDs, while setup-schema.ts accepts only positive integer strings beginning with 1–9. Sharing the validation rule would replace a generic IPC failure with immediate guidance, but this is a localized UX improvement rather than a safety blocker.

Score

The architecture and tests are strong, and current-head status reports no failures, but the four remaining lifecycle and capability defects prevent safe merge. The corrections are localized, though they cover important advertised behavior.

Score: 5/10


🤖 Review Details

  • Model: GPT-5.6 Sol
  • Time: 7m 52s
  • Tokens: 89,173 (74,468 in / 14,705 out)
  • Cost: $0.77

View Task


💡 Next step: Comment /fix to address F# merge blockers only.
F# IDs increment across review comments and remain permanent, so selectors such as /fix F3 F5 stay unambiguous across cycles. Suggestions require a separate ordinary follow-up request.

🤖 Review by ProPR

@integry

integry commented Sep 6, 2026

Copy link
Copy Markdown
Owner

/fix F4 F5 F6 F7

@propr-dev

propr-dev Bot commented Sep 6, 2026

Copy link
Copy Markdown
Contributor Author

Applied the requested follow-up changes in commit 338cc73

Addressed 1 AI review comment (IDs: 5558787894)

Summary of Changes

Implemented only F4–F7:

  • Propagated abort signals through Docker startup, image pulls, status checks, and agent connectivity. Child processes terminate, escalate to SIGKILL, and are reaped before cancellation settles.
  • Revalidated key metadata from the opened descriptor and replaced unbounded reads with a 1 MiB-bounded read.
  • Added separate “Review saved choices” and “Retry setup” recovery actions.
  • Persisted and validated completed local profiles across restarts, with explicit interrupted recovery for invalid state.
  • Captured focused visual preview evidence for the updated recovery screen.

Verification passed:

  • CLI setup: 113 tests
  • Orchestrator: 31 tests
  • Desktop UI: 102 tests
  • Full desktop suite: 503 passed, 25 platform skips
  • CLI, desktop, and UI typechecks
  • CLI and UI lint
  • git diff --check

No commit was created.

Visual preview

Credential recovery actions

Credential recovery actions

Desktop recovery after a credential-bearing failure, showing separate review and transient retry actions at 1280×820.


🤖 Implementation Details

  • Model: GPT-5.6 Sol (high)
  • Requested By: @integry
  • Time: 19m 56s
  • Tokens: 10,504,264 (10,464,315 in / 39,949 out)
  • Cost: $7.48
  • Subscription usage: Weekly +2%

Undo ChangesView Task Execution


💡 ProPR Slash Commands
Command Description Example
/merge Merge target branch into this PR and resolve conflicts /merge
/review Request an AI code review /review or /review claude-sonnet
/fix Implement fixes for issues found by /review /fix or /fix address the null check issue
/switch Change the AI model for this PR /switch claude-opus
/use Override the model for a single follow-up run /use claude-sonnet
/ultrafix Loop review→fix cycles until score goal is met /ultrafix or /ultrafix goal=8 max=10

🤖 Generated by ProPR
Processing comment ID: 5558793662✓

Implemented only F4–F7:

- Propagated abort signals through Docker startup, image pulls, status checks, and agent connectivity. Child processes terminate, escalate to `SIGKILL`, and are reaped before cancellation settles.
- Revalidated key metadata from the opened descriptor and replaced unbounded reads with a 1 MiB-bounded read.
- Added separate “Review saved choices” and “Retry setup” recovery actions.
- Persisted and validated completed local profiles across restarts, with explicit interrupted recovery for invalid state.
- Captured focused visual preview evidence for the updated recovery screen.

Verification passed:

- CLI setup: 113 tests
- Orchestrator: 31 tests
- Desktop UI: 102 tests
- Full desktop suite: 503 passed, 25 platform skips
- CLI, desktop, and UI typechecks
- CLI and UI lint
- `git diff --check`

No commit was created.

PR: #2132
Comment by: @integry (ID: 5558793662)
Model: gpt-5.6-sol
@integry

integry commented Sep 6, 2026

Copy link
Copy Markdown
Owner

/review

@propr-dev

propr-dev Bot commented Sep 6, 2026

Copy link
Copy Markdown
Contributor Author

AI Code Review Complete requested by @integry

Posted 1 review:

View Task Details

@integry

integry commented Sep 6, 2026

Copy link
Copy Markdown
Owner

Independent exact-head follow-up for 338cc73: F5 bounded opened-descriptor reads and F7 validated completion recovery are fixed. F4 has one narrow remaining gap: agentValidation.ts uses fail-fast Promise.all for concurrent version/host/image checks (around638) and another inside versionInfo (around500). Individual child helpers now correctly terminate and await close, but one rejected child allows the aggregate to enter finally/remove temporary resources and return cancellation while another started child is still terminating/reaping.

After the active review finishes, please make a bounded correction that drains all started concurrent checks before cleanup/cancellation settles, preserving the original failure and abort semantics. Add coverage with staggered child termination proving cleanup and cancellation wait for every started check, not only the single Docker pull covered by the existing test. No broader refactor, Windows work, or optional review suggestions. If the active review reports this same issue, treat it as one correction, not two passes. Do not merge.

@propr-dev

propr-dev Bot commented Sep 6, 2026

Copy link
Copy Markdown
Contributor Author

🔍 AI Code Review — codex:gpt-5.6-sol

Overall Evaluation

This PR restores the Linux local-setup journey through a thoughtfully constrained Electron IPC boundary, native secret handling, resumable state, and the existing setup engine. It needs a minor workflow correction before merge; current checks have no failures but remain largely pending.

Strong privilege boundary — Renderer input is schema-validated, filesystem access uses opaque capabilities, and raw secrets remain in the main process.

Thorough cancellation plumbing — Abort signals reach Docker, authentication, agent validation, controller shutdown, and retry paths, with focused tests covering process reaping and recovery.

Authenticated completion flow — Successful setup returns through the existing profile save, strict probe, pairing, activation, and authenticated reprobe flow.

Merge blockers

Every finding below was introduced by this PR and must be resolved before merging.

F8: 🔴 Recovery cannot change ordinary choices

  • Required behavior: The newly added recovery journey must let users correct a configuration that caused setup to fail or be cancelled. Currently, choice review is available only when an opaque credential must be reacquired, leaving other valid failures locked into the same request.
  • Evidence: propr-ui/src/desktop/LocalSetupWizard.tsx:61 — “Review saved choices” is rendered only when snapshot.reconfigurationRequired; lines 118–123 likewise enter editing only under that condition and otherwise call adapter.retry() without a revised request. For example, a failed relay setup cannot be changed to demo mode, and closing/reopening the wizard returns to the same recovery screen.
  • Minimum fix: Offer review or restart whenever a resumable failed, cancelled, or interrupted snapshot has saved choices, then submit the edited choices through retry(request). Credential re-entry can remain mandatory only for modes whose capabilities expired.

Suggestions

These are optional follow-ups and are not sent to /fix.

S1: 🟢 Reconcile cancelled startup services

docker/launcher/orchestrator.mjs skips its existing best-effort rollback when startup is aborted, so services already recorded in started may remain active after cancellation. Resumable setup does not promise transactional rollback, making this optional, but either cleaning up with a fresh non-aborted cleanup context or explicitly documenting the behavior would make “Cancel safely” less surprising.

S2: 🟢 Validate hydrated resume state

apps/desktop/src/setup-controller.ts validates the persisted phase and completed profile but trusts the structure of persisted.resume. Schema-valid JSON with malformed resume fields can leave recovery unable to render or retry. The state is atomically written in a private directory, so this is defensive hardening rather than a normal-path blocker.

S3: 🟢 Clean up superseded key copies

Private-key capabilities copy keys into the setup state directory, but abandoned attempts and later reconfiguration can leave unused copies indefinitely. Tracking and removing demonstrably unreferenced copies would improve sensitive-file lifecycle hygiene without changing the correctness of the current owner-only storage model.

Score

The implementation is substantial, security-conscious, and well tested, but the recovery dead end affects a core journey promised by the PR. Once that localized UI gate is corrected and pending checks finish successfully, it should be close to merge-ready.

Score: 6/10


🤖 Review Details

  • Model: GPT-5.6 Sol
  • Time: 8m 11s
  • Tokens: 93,079 (74,275 in / 18,804 out)
  • Cost: $0.91

View Task


💡 Next step: Comment /fix to address F# merge blockers only.
F# IDs increment across review comments and remain permanent, so selectors such as /fix F3 F5 stay unambiguous across cycles. Suggestions require a separate ordinary follow-up request.

🤖 Review by ProPR

@propr-dev

propr-dev Bot commented Sep 6, 2026

Copy link
Copy Markdown
Contributor Author

Applied the requested follow-up changes in commit b231480

Summary of Changes

Implemented the bounded F4 correction without committing or merging.

  • Both concurrent joins now preserve the first rejection while draining every started check before cleanup: agentValidation.ts.
  • Added staggered subprocess coverage for host/image version and validation checks, verifying cancellation and temporary-resource cleanup wait for the slowest child: cliAgentValidation.test.ts.
  • Updated the existing test orchestrator stub with its required asynchronous Docker method.

Validation passed:

  • Focused cancellation/setup suites: 26 tests
  • CLI typecheck
  • CLI lint
  • CLI build
  • git diff --check

No visual preview was created because the change is not user-visible.


🤖 Implementation Details

  • Model: GPT-5.6 Sol (high)
  • Requested By: @integry
  • Time: 5m 48s
  • Tokens: 1,240,403 (1,229,067 in / 11,336 out)
  • Cost: $1.34
  • Subscription usage: Weekly +1%

Undo ChangesView Task Execution


💡 ProPR Slash Commands
Command Description Example
/merge Merge target branch into this PR and resolve conflicts /merge
/review Request an AI code review /review or /review claude-sonnet
/fix Implement fixes for issues found by /review /fix or /fix address the null check issue
/switch Change the AI model for this PR /switch claude-opus
/use Override the model for a single follow-up run /use claude-sonnet
/ultrafix Loop review→fix cycles until score goal is met /ultrafix or /ultrafix goal=8 max=10

🤖 Generated by ProPR
Processing comment ID: 5558903961✓

@integry

integry commented Sep 6, 2026

Copy link
Copy Markdown
Owner

/fix F8

@github-actions

github-actions Bot commented Sep 6, 2026

Copy link
Copy Markdown

Full Test Suite Results

Validation failed during: Full test suite.

  • Dependency install: success
  • Workspace build: success
  • Docs validation: success
  • Redis startup: success
  • Full test suite: failure
View Test Output

> propr@0.8.15 test:full:prepared
> npm run test:server


> propr@0.8.15 test:server
> node scripts/run-test-suite.mjs


[1/392] apps/desktop/scripts/assert-windows-mvp-package.test.mjs
TAP version 13
# Subtest: Windows MVP ASAR main entry
    # Subtest: uses the rooted listPackage representation accepted by extractFile
    ok 1 - uses the rooted listPackage representation accepted by extractFile
      ---
      duration_ms: 27.755557
      type: 'test'
      ...
    # Subtest: preserves the Windows separator after removing the one archive root
    ok 2 - preserves the Windows separator after removing the one archive root
      ---
      duration_ms: 0.214509
      type: 'test'
      ...
    # Subtest: rejects traversal, duplicate entries, and case-colliding main paths
    ok 3 - rejects traversal, duplicate entries, and case-colliding main paths
      ---
      duration_ms: 0.663266
      type: 'test'
      ...
    1..3
ok 1 - Windows MVP ASAR main entry
  ---
  duration_ms: 29.767845
  type: 'suite'
  ...
1..1
# tests 3
# suites 1
# pass 3
# fail 0
# cancelled 0
# skipped 0
# todo 0
# duration_ms 283.482336

[2/392] apps/desktop/scripts/build-windows-machine-installer.test.mjs
TAP version 13
# Subtest: sets explicit Windows-1252 MSI and summary code pages in probe and production WXS
ok 1 - sets explicit Windows-1252 MSI and summary code pages in probe and production WXS
  ---
  duration_ms: 1.574473
  type: 'test'
  ...
# Subtest: accepts the exact MSI ProductVersion boundary and retains version and upgrade identity in WXS
ok 2 - accepts the exact MSI ProductVersion boundary and retains version and upgrade identity in WXS
  ---
  duration_ms: 0.541989
  type: 'test'
  ...
# Subtest: rejects every unsupported ProductVersion at the direct installer builder entry point
ok 3 - rejects every unsupported ProductVersion at the direct installer builder entry point
  ---
  duration_ms: 2.154534
  type: 'test'
  ...
# Subtest: uses per-machine scope without explicitly authoring the derived ALLUSERS property
ok 4 - uses per-machine scope without explicitly authoring the derived ALLUSERS property
  ---
  duration_ms: 0.3753
  type: 'test'
  ...
# Subtest: authors the complete per-machine Start Menu contract for x64 and ARM64
ok 5 - authors the complete per-machine Start Menu contract for x64 and ARM64
  ---
  duration_ms: 0.81087
  type: 'test'
  ...
# Subtest: selects only the installed x64 WiX directory or an explicit ARM64 build directory
ok 6 - selects only the installed x64 WiX directory or an explicit ARM64 build directory
  ---
  duration_ms: 0.538473
  type: 'test'
  ...
# Subtest: uses a ten-minute timeout only for production Light
ok 7 - uses a ten-minute timeout only for production Light
  ---
  duration_ms: 0.287786
  type: 'test'
  ...
# Subtest: keeps WiX processes and their emitted diagnostics bounded
ok 8 - keeps WiX processes and their emitted diagnostics bounded
  ---
  duration_ms: 0.149449
  type: 'test'
  ...
# Subtest: emits WiX v3 default registry values without empty Name attributes
ok 9 - emits WiX v3 default registry values without empty Name attributes
  ---
  duration_ms: 0.390297
  type: 'test'
  ...
1..9
# tests 9
# suites 0
# pass 9
# fail 0
# cancelled 0
# skipped 0
# todo 0
# duration_ms 238.402247

[3/392] apps/desktop/scripts/packaged-connect-evidence.test.mjs
TAP version 13
# Subtest: packaged Connect aggregate evidence
    # Subtest: accepts the complete fixed protocol evidence
    ok 1 - accepts the complete fixed protocol evidence
      ---
      duration_ms: 1.238938
      type: 'test'
      ...
    # Subtest: accepts valid rotated Socket.IO bindings alongside the expected stale-auth rejection
    ok 2 - accepts valid rotated Socket.IO bindings alongside the expected stale-auth rejection
      ---
      duration_ms: 0.74072
      type: 'test'
      ...
    # Subtest: requires exactly eight pair discoveries and two fresh-process reprobe discoveries
    ok 3 - requires exactly eight pair discoveries and two fresh-process reprobe discoveries
      ---
      duration_ms: 0.213157
      type: 'test'
      ...
    # Subtest: reports only fixed evidence for DISCOVERY_COUNT_MISMATCH
    ok 4 - reports only fixed evidence for DISCOVERY_COUNT_MISMATCH
      ---
      duration_ms: 0.368837
      type: 'test'
      ...
    # Subtest: reports only fixed evidence for DISCOVERY_AUTHORIZATION_PRESENT
    ok 5 - reports only fixed evidence for DISCOVERY_AUTHORIZATION_PRESENT
      ---
      duration_ms: 0.1661
      type: 'test'
      ...
    # Subtest: reports only fixed evidence for PAIRING_START_MISSING
    ok 6 - reports only fixed evidence for PAIRING_START_MISSING
      ---
      duration_ms: 0.223256
      type: 'test'
      ...
    # Subtest: reports only fixed evidence for PAIRING_START_DUPLICATE
    ok 7 - reports only fixed evidence for PAIRING_START_DUPLICATE
      ---
      duration_ms: 0.201636
      type: 'test'
      ...
    # Subtest: reports only fixed evidence for PAIRING_BROWSER_COUNT_MISMATCH
    ok 8 - reports only fixed evidence for PAIRING_BROWSER_COUNT_MISMATCH
      ---
      duration_ms: 0.173784
      type: 'test'
      ...
    # Subtest: reports only fixed evidence for PAIRING_POLL_COUNT_MISMATCH
    ok 9 - reports only fixed evidence for PAIRING_POLL_COUNT_MISMATCH
      ---
      duration_ms: 0.255717
      type: 'test'
      ...
    # Subtest: reports only fixed evidence for PAIRING_ACTIVATION_COUNT_MISMATCH
    ok 10 - reports only fixed evidence for PAIRING_ACTIVATION_COUNT_MISMATCH
      ---
      duration_ms: 0.351535
      type: 'test'
      ...
    # Subtest: reports only fixed evidence for PAIRING_METHOD_MISMATCH
    ok 11 - reports only fixed evidence for PAIRING_METHOD_MISMATCH
      ---
      duration_ms: 0.133769
      type: 'test'
      ...
    # Subtest: reports only fixed evidence for PAIRING_BROWSER_CREDENTIAL_PRESENT
    ok 12 - reports only fixed evidence for PAIRING_BROWSER_CREDENTIAL_PRESENT
      ---
      duration_ms: 0.179324
      type: 'test'
      ...
    # Subtest: reports only fixed evidence for PAIRING_INTENT_SEQUENCE_MISMATCH
    ok 13 - reports only fixed evidence for PAIRING_INTENT_SEQUENCE_MISMATCH
      ---
      duration_ms: 0.130183
      type: 'test'
      ...
    # Subtest: reports only fixed evidence for PAIRING_LIFECYCLE_ISOLATION_FAILED
    ok 14 - reports only fixed evidence for PAIRING_LIFECYCLE_ISOLATION_FAILED
      ---
      duration_ms: 0.096309
      type: 'test'
      ...
    # Subtest: reports only fixed evidence for PAIRING_REQUEST_AFTER_TERMINAL
    ok 15 - reports only fixed evidence for PAIRING_REQUEST_AFTER_TERMINAL
      ---
      duration_ms: 0.120896
      type: 'test'
      ...
    # Subtest: reports only fixed evidence for DELAYED_APPROVAL_READINESS_MISSING
    ok 16 - reports only fixed evidence for DELAYED_APPROVAL_READINESS_MISSING
      ---
      duration_ms: 0.092041
      type: 'test'
      ...
    # Subtest: reports only fixed evidence for BOOTSTRAP_AUTHORIZATION_PRESENT
    ok 17 - reports only fixed evidence for BOOTSTRAP_AUTHORIZATION_PRESENT
      ---
      duration_ms: 0.086712
      type: 'test'
      ...
    # Subtest: reports only fixed evidence for AUTHENTICATED_REST_COUNT_MISMATCH
    ok 18 - reports only fixed evidence for AUTHENTICATED_REST_COUNT_MISMATCH
      ---
      duration_ms: 0.084938
      type: 'test'
      ...
    # Subtest: reports only fixed evidence for AUTHENTICATED_SOCKET_COUNT_MISMATCH
    ok 19 - reports only fixed evidence for AUTHENTICATED_SOCKET_COUNT_MISMATCH
      ---
      duration_ms: 0.087964
      type: 'test'
      ...
    # Subtest: reports only fixed evidence for REST_SCOPE_MISMATCH
    ok 20 - reports only fixed evidence for REST_SCOPE_MISMATCH
      ---
      duration_ms: 0.08572
      type: 'test'
      ...
    # Subtest: reports only fixed evidence for SOCKET_SCOPE_MISSING
    ok 21 - reports only fixed evidence for SOCKET_SCOPE_MISSING
      ---
      duration_ms: 0.187569
      type: 'test'
      ...
    # Subtest: reports only fixed evidence for SOCKET_SCOPE_BINDING_MISMATCH
    ok 22 - reports only fixed evidence for SOCKET_SCOPE_BINDING_MISMATCH
      ---
      duration_ms: 0.092392
      type: 'test'
      ...
    # Subtest: reports only fixed evidence for SOCKET_SCOPE_ROTATION_MISMATCH
    ok 23 - reports only fixed evidence for SOCKET_SCOPE_ROTATION_MISMATCH
      ---
      duration_ms: 0.111528
      type: 'test'
      ...
    # Subtest: reports only fixed evidence for PLAINTEXT_CREDENTIAL_PERSISTED
    ok 24 - reports only fixed evidence for PLAINTEXT_CREDENTIAL_PERSISTED
      ---
      duration_ms: 0.106819
      type: 'test'
      ...
    # Subtest: reports only fixed evidence for PUBLIC_IDENTITY_MISSING
    ok 25 - reports only fixed evidence for PUBLIC_IDENTITY_MISSING
      ---
      duration_ms: 0.081521
      type: 'test'
      ...
    # Subtest: reports only fixed evidence for PUBLIC_IDENTITY_ORDER_MISMATCH
    ok 26 - reports only fixed evidence for PUBLIC_IDENTITY_ORDER_MISMATCH
      ---
      duration_ms: 0.108483
      type: 'test'
      ...
    1..26
ok 1 - packaged Connect aggregate evidence
  ---
  duration_ms: 8.100511
  type: 'suite'
  ...
1..1
# tests 26
# suites 1
# pass 26
# fail 0
# cancelled 0
# skipped 0
# todo 0
# duration_ms 243.643936

[4/392] apps/desktop/scripts/packaged-connect-launch.test.mjs
TAP version 13
# Subtest: packaged Connect launch boundary
    # Subtest: passes the one effective Linux argv through the actual binary spawn
    ok 1 - passes the one effective Linux argv through the actual binary spawn
      ---
      duration_ms: 1.306043
      type: 'test'
      ...
    # Subtest: does not add the Linux password-store selection on Darwin
    ok 2 - does not add the Linux password-store selection on Darwin
      ---
      duration_ms: 0.144118
      type: 'test'
      ...
    # Subtest: the lifecycle and real binary spawn share the derived argv source
    ok 3 - the lifecycle and real binary spawn share the derived argv source
      ---
      duration_ms: 4.927179
      type: 'test'
      ...
    1..3
ok 1 - packaged Connect launch boundary
  ---
  duration_ms: 7.521203
  type: 'suite'
  ...
1..1
# tests 3
# suites 1
# pass 3
# fail 0
# cancelled 0
# skipped 0
# todo 0
# duration_ms 222.695067

[5/392] apps/desktop/scripts/packaged-connect-lifecycle.test.mjs
TAP version 13
# Subtest: packaged Connect bounded child lifecycle
    # Subtest: requires exact three starts, three browser approvals, one poll, and one activation
    ok 1 - requires exact three starts, three browser approvals, one poll, and one activation
      ---
      duration_ms: 10.591242
      type: 'test'
      ...
    # Subtest: accepts an exact ready proof followed by a clean exit
    ok 2 - accepts an exact ready proof followed by a clean exit
      ---
      duration_ms: 4.177303
      type: 'test'
      ...
    # Subtest: requires and preserves the expected fixed storage-backend report before readiness
    ok 3 - requires and preserves the expected fixed storage-backend report before readiness
      ---
      duration_ms: 13.372744
      type: 'test'
      ...
    # Subtest: does not accept an intermediate discovery milestone as terminal readiness
    ok 4 - does not accept an intermediate discovery milestone as terminal readiness
      ---
      duration_ms: 1.184957
      type: 'test'
      ...
    # Subtest: returns only exact allowlisted journey stages
    ok 5 - returns only exact allowlisted journey stages
      ---
      duration_ms: 1.880894
      type: 'test'
      ...
    # Subtest: retains the latest bounded journey stage when earlier diagnostics fill the cap
    ok 6 - retains the latest bounded journey stage when earlier diagnostics fill the cap
      ---
      duration_ms: 2.22241
      type: 'test'
      ...
    # Subtest: retains only fixed terminal journey failure evidence when diagnostics fill the cap
    ok 7 - retains only fixed terminal journey failure evidence when diagnostics fill the cap
      ---
      duration_ms: 3.96287
      type: 'test'
      ...
    # Subtest: drops non-allowlisted terminal journey failure fields
    ok 8 - drops non-allowlisted terminal journey failure fields
      ---
      duration_ms: 1.284504
      type: 'test'
      ...
    # Subtest: returns only fixed secret-free Local Network Access decision evidence
    ok 9 - returns only fixed secret-free Local Network Access decision evidence
      ---
      duration_ms: 1.623114
      type: 'test'
      ...
    # Subtest: fails closed when an otherwise allowlisted journey stage contains a secret
    ok 10 - fails closed when an otherwise allowlisted journey stage contains a secret
      ---
      duration_ms: 1.159741
      type: 'test'
      ...
    # Subtest: publishes the sole terminal READY only after each real journey phase
    ok 11 - publishes the sole terminal READY only after each real journey phase
      ---
      duration_ms: 7.789762
      type: 'test'
      ...
    # Subtest: forces a ready app with a hung descendant through an exact bounded taskkill invocation
    ok 12 - forces a ready app with a hung descendant through an exact bounded taskkill invocation
      ---
      duration_ms: 6.969174
      type: 'test'
      ...
    # Subtest: keeps timeout-before-ready primary while terminating and draining the tree
    ok 13 - keeps timeout-before-ready primary while terminating and draining the tree
      ---
      duration_ms: 15.513673
      type: 'test'
      ...
    # Subtest: classifies asynchronous spawn errors without exposing their message
    ok 14 - classifies asynchronous spawn errors without exposing their message
      ---
      duration_ms: 5.804033
      type: 'test'
      ...
    # Subtest: settles close/timeout races once and never upgrades an early exit to success
    ok 15 - settles close/timeout races once and never upgrades an early exit to success
      ---
      duration_ms: 0.668054
      type: 'test'
      ...
    # Subtest: accepts a clean post-proof close racing a taskkill no-process result
    ok 16 - accepts a clean post-proof close racing a taskkill no-process result
      ---
      duration_ms: 6.348668
      type: 'test'
      ...
    # Subtest: rejects malformed, partial, truncated, and extra-field ready records
    ok 17 - rejects malformed, partial, truncated, and extra-field ready records
      ---
      duration_ms: 8.407414
      type: 'test'
      ...
    # Subtest: terminates an exact-event record whose platform proof is invalid
    ok 18 - terminates an exact-event record whose platform proof is invalid
      ---
      duration_ms: 1.584051
      type: 'test'
      ...
    # Subtest: fails after proof when Windows tree termination cannot be proven
    ok 19 - fails after proof when Windows tree termination cannot be proven
      ---
      duration_ms: 5.772955
      type: 'test'
      ...
    # Subtest: never returns secret-bearing raw output or non-allowlisted record fields
    ok 20 - never returns secret-bearing raw output or non-allowlisted record fields
      ---
      duration_ms: 1.263945
      type: 'test'
      ...
    # Subtest: revokes success when sensitive output arrives after the exact ready proof
    ok 21 - revokes success when sensitive output arrives after the exact ready proof
      ---
      duration_ms: 1.12715
      type: 'test'
      ...
    # Subtest: rejects a JSON-escaped Windows path in a non-allowlisted record before readiness
    ok 22 - rejects a JSON-escaped Windows path in a non-allowlisted record before readiness
      ---
      duration_ms: 0.935162
      type: 'test'
      ...
    # Subtest: revokes success for a JSON-escaped Windows path after the exact ready proof
    ok 23 - revokes success for a JSON-escaped Windows path after the exact ready proof
      ---
      duration_ms: 0.821219
      type: 'test'
      ...
    # Subtest: revokes success when a JSON-escaped Windows path follows the record-count cap
    ok 24 - revokes success when a JSON-escaped Windows path follows the record-count cap
      ---
      duration_ms: 2.666097
      type: 'test'
      ...
    # Subtest: revokes success when a JSON-escaped Windows path follows the byte cap
    ok 25 - revokes success when a JSON-escaped Windows path follows the byte cap
      ---
      duration_ms: 10.10706
      type: 'test'
      ...
    1..25
ok 1 - packaged Connect bounded child lifecycle
  ---
  duration_ms: 119.788687
  type: 'suite'
  ...
# Subtest: packaged Connect fixture cleanup
    # Subtest: closes the journey fixture once and tolerates only the already-stopped server condition
    ok 1 - closes the journey fixture once and tolerates only the already-stopped server condition
      ---
      duration_ms: 1.271038
      type: 'test'
      ...
    # Subtest: retries a transient Windows EBUSY only inside the authorized fixture
    ok 2 - retries a transient Windows EBUSY only inside the authorized fixture
      ---
      duration_ms: 5.671396
      type: 'test'
      ...
    # Subtest: redacts cleanup failure and preserves the primary lifecycle outcome
    ok 3 - redacts cleanup failure and preserves the primary lifecycle outcome
      ---
      duration_ms: 1.152247
      type: 'test'
      ...
    # Subtest: bounds a never-settling removal and preserves the primary result
    ok 4 - bounds a never-settling removal and preserves the primary result
      ---
      duration_ms: 10.216023
      type: 'test'
      ...
    # Subtest: bounds a never-settling authorization call as a fixed cleanup failure
    ok 5 - bounds a never-settling authorization call as a fixed cleanup failure
      ---
      duration_ms: 10.945281
      type: 'test'
      ...
    # Subtest: isolates default Windows filesystem cleanup from the harness process
    ok 6 - isolates default Windows filesystem cleanup from the harness process
      ---
      duration_ms: 44.148171
      type: 'test'
      ...
    # Subtest: refuses a link, renamed leaf, or fixture outside the canonical temporary parent
    ok 7 - refuses a link, renamed leaf, or fixture outside the canonical temporary parent
      ---
      duration_ms: 0.512355
      type: 'test'
      ...
    1..7
ok 2 - packaged Connect fixture cleanup
  ---
  duration_ms: 76.995355
  type: 'suite'
  ...
1..2
# tests 32
# suites 2
# pass 32
# fail 0
# cancelled 0
# skipped 0
# todo 0
# duration_ms 416.822264

[6/392] apps/desktop/scripts/packaged-connect-platform.test.mjs
TAP version 13
# Subtest: packaged Connect target-native credential setup
    # Subtest: Linux retains one isolated unlocked libsecret session and rejects plaintext fallback
    ok 1 - Linux retains one isolated unlocked libsecret session and rejects plaintext fallback
      ---
      duration_ms: 5.403878
      type: 'test'
      ...
    # Subtest: inspects the ordinary unsigned package before adding the Darwin-only acceptance identity
    ok 2 - inspects the ordinary unsigned package before adding the Darwin-only acceptance identity
      ---
      duration_ms: 0.702879
      type: 'test'
      ...
    # Subtest: Darwin creates one ephemeral certificate-backed identity and proves it across both launches
    ok 3 - Darwin creates one ephemeral certificate-backed identity and proves it across both launches
      ---
      duration_ms: 0.877445
      type: 'test'
      ...
    # Subtest: Darwin root signing sets, but never preserves, the required identifier
    ok 4 - Darwin root signing sets, but never preserves, the required identifier
      ---
      duration_ms: 0.22556
      type: 'test'
      ...
    # Subtest: Darwin emits only allowlisted fixed stage markers around every blocking phase
    ok 5 - Darwin emits only allowlisted fixed stage markers around every blocking phase
      ---
      duration_ms: 14.51753
      type: 'test'
      ...
    # Subtest: Darwin bounds setup, nested signing, verification, journey, cleanup, and the wrapper
    ok 6 - Darwin bounds setup, nested signing, verification, journey, cleanup, and the wrapper
      ---
      duration_ms: 0.350343
      type: 'test'
      ...
    # Subtest: Darwin failure diagnostics are fixed, classified, and secret-safe
    ok 7 - Darwin failure diagnostics are fixed, classified, and secret-safe
      ---
      duration_ms: 0.670259
      type: 'test'
      ...
    # Subtest: Darwin restores keychain state and deletes identity, credentials, and files on every exit
    ok 8 - Darwin restores keychain state and deletes identity, credentials, and files on every exit
      ---
      duration_ms: 0.484191
      type: 'test'
      ...
    # Subtest: Darwin smoke has no static or production identity and does not widen or pre-seed Safe Storage
    ok 9 - Darwin smoke has no static or production identity and does not widen or pre-seed Safe Storage
      ---
      duration_ms: 4.738679
      type: 'test'
      ...
    1..9
ok 1 - packaged Connect target-native credential setup
  ---
  duration_ms: 29.595791
  type: 'suite'
  ...
1..1
# tests 9
# suites 1
# pass 9
# fail 0
# cancelled 0
# skipped 0
# todo 0
# duration_ms 245.349473

[7/392] apps/desktop/scripts/packaged-layout.test.mjs
TAP version 13
# Subtest: packaged desktop event parsing
    # Subtest: returns the first full record for the exact matching event
    ok 1 - returns the first full record for the exact matching event
      ---
      duration_ms: 1.639825
      type: 'test'
      ...
    # Subtest: returns undefined when the event is absent
    ok 2 - returns undefined when the event is absent
      ---
      duration_ms: 0.20942
      type: 'test'
      ...
    1..2
ok 1 - packaged desktop event parsing
  ---
  duration_ms: 2.951489
  type: 'suite'
  ...
# Subtest: packaged desktop layout assertions
    # Subtest: retains the exact 1280x820 Linux Xvfb proof
    ok 1 - retains the exact 1280x820 Linux Xvfb proof
      ---
      duration_ms: 1.219983
      type: 'test'
      ...
    # Subtest: accepts a safe 1024x720 Windows display clamp with intact contained content
    ok 2 - accepts a safe 1024x720 Windows display clamp with intact contained content
      ---
      duration_ms: 0.232543
      type: 'test'
      ...
    # Subtest: rejects unsafe Windows clamps and content outside the visible work area
    ok 3 - rejects unsafe Windows clamps and content outside the visible work area
      ---
      duration_ms: 0.302604
      type: 'test'
      ...
    1..3
ok 2 - packaged desktop layout assertions
  ---
  duration_ms: 2.076569
  type: 'suite'
  ...
1..2
# tests 5
# suites 2
# pass 5
# fail 0
# cancelled 0
# skipped 0
# todo 0
# duration_ms 237.166936

[8/392] apps/desktop/scripts/packaged-smoke-support.test.mjs
TAP version 13
# Subtest: packaged smoke native window layout
    # Subtest: accepts the preferred size while retaining responsive containment
    ok 1 - accepts the preferred size while retaining responsive containment
      ---
      duration_ms: 1.328225
      type: 'test'
      ...
    # Subtest: accepts the 1024x720-clamped size while retaining responsive containment
    ok 2 - accepts the 1024x720-clamped size while retaining responsive containment
      ---
      duration_ms: 0.197327
      type: 'test'
      ...
    # Subtest: accepts the configured minimum size while retaining responsive containment
    ok 3 - accepts the configured minimum size while retaining responsive containment
      ---
      duration_ms: 0.151111
      type: 'test'
      ...
    # Subtest: accepts the undersized work area while retaining responsive containment
    ok 4 - accepts the undersized work area while retaining responsive containment
      ---
      duration_ms: 0.150681
      type: 'test'
      ...
    # Subtest: rejects an unclamped window or a viewport inconsistent with native content chrome
    ok 5 - rejects an unclamped window or a viewport inconsistent with native content chrome
      ---
      duration_ms: 0.54202
      type: 'test'
      ...
    # Subtest: accepts actual reduced native sizing only when both minimum constraints are exercised
    ok 6 - accepts actual reduced native sizing only when both minimum constraints are exercised
      ---
      duration_ms: 0.272287
      type: 'test'
      ...
    1..6
ok 1 - packaged smoke native window layout
  ---
  duration_ms: 3.941884
  type: 'suite'
  ...
# Subtest: packaged smoke child environment
    # Subtest: defines four isolated launches with exact per-mode environment, argv, and marker contracts
    ok 1 - defines four isolated launches with exact per-mode environment, argv, and marker contracts
      ---
      duration_ms: 1.424163
      type: 'test'
      ...
    # Subtest: passes only platform launch inputs and private profile paths from a hostile parent
    ok 2 - passes only platform launch inputs and private profile paths from a hostile parent
      ---
      duration_ms: 19.602059
      type: 'test'
      ...
    # Subtest: keeps cleanup bounded to the generated profile root
    ok 3 - keeps cleanup bounded to the generated profile root
      ---
      duration_ms: 17.443561
      type: 'test'
      ...
    # Subtest: accepts only a normalized absolute Windows SystemRoot directory
    ok 4 - accepts only a normalized absolute Windows SystemRoot directory
      ---
      duration_ms: 1.690049
      type: 'test'
      ...
    # Subtest: contains no parent environment spread, enumeration, denylist, PATH, or shell launch
    ok 5 - contains no parent environment spread, enumeration, denylist, PATH, or shell launch
      ---
      duration_ms: 3.277596
      type: 'test'
      ...
    # Subtest: serves each named fixture identity paired with its persisted credential
    ok 6 - serves each named fixture identity paired with its persisted credential
      ---
      duration_ms: 3.160114
      type: 'test'
      ...
    # Subtest: requires the adjacent packaged spawn options with LF or CRLF source
    ok 7 - requires the adjacent packaged spawn options with LF or CRLF source
      ---
      duration_ms: 1.756904
      type: 'test'
      ...
    1..7
ok 2 - packaged smoke child environment
  ---
  duration_ms: 48.956357
  type: 'suite'
  ...
1..2
# tests 13
# suites 2
# pass 13
# fail 0
# cancelled 0
# skipped 0
# todo 0
# duration_ms 287.344834

[9/392] apps/desktop/scripts/release-architecture.test.mjs
TAP version 13
# Subtest: machine-wide Windows artifacts require a real MSI compound file
ok 1 - machine-wide Windows artifacts require a real MSI compound file
  ---
  duration_ms: 7.2609
  type: 'test'
  ...
# Subtest: administrative MSI payload inspection
    # Subtest: uses exact fixed native extractor argv and minimal environments
    ok 1 - uses exact fixed native extractor argv and minimal environments
      ---
      duration_ms: 1.289593
      type: 'test'
      ...
    # Subtest: accepts only the canonical application with the one administrative root prefix
    ok 2 - accepts only the canonical application with the one administrative root prefix
      ---
      duration_ms: 17.433299
      type: 'test'
      ...
    # Subtest: rejects path escapes and case collisions from the Linux listing before extraction
    ok 3 - rejects path escapes and case collisions from the Linux listing before extraction
      ---
      duration_ms: 1.107383
      type: 'test'
      ...
    # Subtest: uses fixed missing and duplicate canonical-app codes with bounded counts
    ok 4 - uses fixed missing and duplicate canonical-app codes with bounded counts
      ---
      duration_ms: 12.056632
      type: 'test'
      ...
    # Subtest: distinguishes authority resources, unsafe trees, and architecture mismatch without path data
    ok 5 - distinguishes authority resources, unsafe trees, and architecture mismatch without path data
      ---
      duration_ms: 11.493913
      type: 'test'
      ...
    # Subtest: maps extractor failures to one redacted tool code
    ok 6 - maps extractor failures to one redacted tool code
      ---
      duration_ms: 3.037339
      type: 'test'
      ...
    # Subtest: retains compound-file, per-machine scope, and canonical PE evidence across extraction
    ok 7 - retains compound-file, per-machine scope, and canonical PE evidence across extraction
      ---
      duration_ms: 5.49093
      type: 'test'
      ...
    # Subtest: fails closed on extractor nonzero, stderr, output overflow, and timeout
    ok 8 - fails closed on extractor nonzero, stderr, output overflow, and timeout
      ---
      duration_ms: 136.478421
      type: 'test'
      ...
    1..8
ok 2 - administrative MSI payload inspection
  ---
  duration_ms: 189.524667
  type: 'suite'
  ...
# Subtest: DEB and RPM executable layouts
    # Subtest: accept only the canonical regular ELF payload and documented launcher symlink
    ok 1 - accept only the canonical regular ELF payload and documented launcher symlink
      ---
      duration_ms: 18.252615
      type: 'test'
      ...
    # Subtest: reject missing and extra payload names for both package formats
    ok 2 - reject missing and extra payload names for both package formats
      ---
      duration_ms: 15.943153
      type: 'test'
      ...
    # Subtest: reject unexpected same-name file types and non-ELF or cross-architecture payloads
    ok 3 - reject unexpected same-name file types and non-ELF or cross-architecture payloads
      ---
      duration_ms: 18.84045
      type: 'test'
      ...
    # Subtest: reject launcher escapes, cycles, and targets other than the canonical payload
    ok 4 - reject launcher escapes, cycles, and targets other than the canonical payload
      ---
      duration_ms: 11.860199
      type: 'test'
      ...
    # Subtest: reject special files with the executable name
    ok 5 - reject special files with the executable name
      ---
      duration_ms: 19.605926
      type: 'test'
      ...
    1..5
ok 3 - DEB and RPM executable layouts
  ---
  duration_ms: 84.867934
  type: 'suite'
  ...
# Subtest: DMG application layout
    # Subtest: accepts the real Forge tree with its install link and nested Electron helper bundles
    ok 1 - accepts the real Forge tree with its install link and nested Electron helper bundles
      ---
      duration_ms: 15.733098
      type: 'test'
      ...
    # Subtest: rejects a symbolic-link canonical helper bundle
    ok 2 - rejects a symbolic-link canonical helper bundle
      ---
      duration_ms: 8.166908
      type: 'test'
      ...
    # Subtest: rejects a symbolic-link canonical helper executable ancestor
    ok 3 - rejects a symbolic-link canonical helper executable ancestor
      ---
      duration_ms: 8.553593
      type: 'test'
      ...
    # Subtest: rejects every symbolic link outside canonical framework internals
    ok 4 - rejects every symbolic link outside canonical framework internals
      ---
      duration_ms: 10.678447
      type: 'test'
      ...
    # Subtest: rejects escaping, cyclic, missing, and case-mismatched framework symbolic links
    ok 5 - rejects escaping, cyclic, missing, and case-mismatched framework symbolic links
      ---
      duration_ms: 43.390494
      type: 'test'
      ...
    # Subtest: never treats Linux 7z sanitized install-link output as native layout evidence
    ok 6 - never treats Linux 7z sanitized install-link output as native layout evidence
      ---
      duration_ms: 10.213171
      type: 'test'
      ...
    # Subtest: rejects wrong bundles, alternate same-name executables, and canonical symlink escapes
    ok 7 - rejects wrong bundles, alternate same-name executables, and canonical symlink escapes
      ---
      duration_ms: 15.128782
      type: 'test'
      ...
    # Subtest: rejects alternate top-level application bundles
    ok 8 - rejects alternate top-level application bundles
      ---
      duration_ms: 6.694535
      type: 'test'
      ...
    # Subtest: rejects unsafe links inside the canonical application bundle
    ok 9 - rejects unsafe links inside the canonical application bundle
      ---
      duration_ms: 12.240832
      type: 'test'
      ...
    # Subtest: rejects non-helper nested application bundles
    ok 10 - rejects non-helper nested application bundles
      ---
      duration_ms: 11.592598
      type: 'test'
      ...
    # Subtest: rejects case-colliding top-level entries when the filesystem permits them
    ok 11 - rejects case-colliding top-level entries when the filesystem permits them
      ---
      duration_ms: 7.764896
      type: 'test'
      ...
    # Subtest: rejects special files inside the canonical application bundle
    ok 12 - rejects special files inside the canonical application bundle
      ---
      duration_ms: 15.423354
      type: 'test'
      ...
    1..12
ok 4 - DMG application layout
  ---
  duration_ms: 166.221944
  type: 'suite'
  ...
1..4
# tests 26
# suites 3
# pass 26
# fail 0
# cancelled 0
# skipped 0
# todo 0
# duration_ms 671.654479

[10/392] apps/desktop/scripts/release-artifacts.test.mjs
TAP version 13
# Subtest: desktop release artifacts
    # Subtest: dry-runs the canonical macOS/Linux profile through finalize and signed update metadata
    ok 1 - dry-runs the canonical macOS/Linux profile through finalize and signed update metadata
      ---
      duration_ms: 114.520074
      type: 'test'
      ...
    # Subtest: fails closed for missing profiles and rejects Windows fragments in the macOS/Linux profile
    ok 2 - fails closed for missing profiles and rejects Windows fragments in the macOS/Linux profile
      ---
      duration_ms: 76.230249
      type: 'test'
      ...
    # Subtest: stages named artifacts and finalizes unsigned validation metadata
    ok 3 - stages named artifacts and finalizes unsigned validation metadata
      ---
      duration_ms: 49.21079
      type: 'test'
      ...
    # Subtest: rejects extensionless, doubled-extension, case-conflicting, duplicate, wrong-kind, stale, and mixed-target names
    ok 4 - rejects extensionless, doubled-extension, case-conflicting, duplicate, wrong-kind, stale, and mixed-target names
      ---
      duration_ms: 263.864878
      type: 'test'
      ...
    # Subtest: rejects altered DMG bytes even when fragment artifact metadata is rewritten
    ok 5 - rejects altered DMG bytes even when fragment artifact metadata is rewritten
      ---
      duration_ms: 39.851304
      type: 'test'
      ...
    # Subtest: rejects permanent DMG replacement or in-place mutation during held inspection without emitting evidence
    ok 6 - rejects permanent DMG replacement or in-place mutation during held inspection without emitting evidence
      ---
      duration_ms: 12.250804
      type: 'test'
      ...
    # Subtest: keeps held A bytes, evidence, and publication stable when original and public pathnames change during inspection
    ok 7 - keeps held A bytes, evidence, and publication stable when original and public pathnames change during inspection
      ---
      duration_ms: 10.212576
      type: 'test'
      ...
    # Subtest: continues to reject a mutable pathname passed directly to DMG inspection
    ok 8 - continues to reject a mutable pathname passed directly to DMG inspection
      ---
      duration_ms: 0.26844
      type: 'test'
      ...
    # Subtest: requires explicit fixture authority for Windows-hosted DMG evidence tests
    ok 9 - requires explicit fixture authority for Windows-hosted DMG evidence tests # SKIP
      ---
      duration_ms: 0.119794
      type: 'test'
      ...
    # Subtest: accepts real Darwin mode-0700 directory and mode-0600 single-link file authority
    ok 10 - accepts real Darwin mode-0700 directory and mode-0600 single-link file authority # SKIP
      ---
      duration_ms: 0.184183
      type: 'test'
      ...
    # Subtest: rejects native Darwin broad mode, foreign owner, extra link, replacement type, and symlink with fixed authority codes
    ok 11 - rejects native Darwin broad mode, foreign owner, extra link, replacement type, and symlink with fixed authority codes # SKIP
      ---
      duration_ms: 0.066124
      type: 'test'
      ...
    # Subtest: accepts native xattr/ctime-only change when held bytes and identity are unchanged
    ok 12 - accepts native xattr/ctime-only change when held bytes and identity are unchanged # SKIP
      ---
      duration_ms: 0.104044
      type: 'test'
      ...
    # Subtest: does not emit claimed DMG layout evidence without the native-validation marker
    ok 13 - does not emit claimed DMG layout evidence without the native-validation marker
      ---
      duration_ms: 10.47576
      type: 'test'
      ...
    # Subtest: strictly rejects missing, mixed, stale, malformed, or fabricated native DMG evidence
    ok 14 - strictly rejects missing, mixed, stale, malformed, or fabricated native DMG evidence
      ---
      duration_ms: 341.84657
      type: 'test'
      ...
    # Subtest: rejects native DMG evidence copied between x64 and arm64 fragments
    ok 15 - rejects native DMG evidence copied between x64 and arm64 fragments
      ---
      duration_ms: 34.785162
      type: 'test'
      ...
    # Subtest: rejects duplicate target fragments before aggregation
    ok 16 - rejects duplicate target fragments before aggregation
      ---
      duration_ms: 21.362452
      type: 'test'
      ...
    # Subtest: rejects either Windows fragment when the installed ordinary-user application gate was skipped
    ok 17 - rejects either Windows fragment when the installed ordinary-user application gate was skipped
      ---
      duration_ms: 74.692842
      type: 'test'
      ...
    # Subtest: fails closed when trusted update signing configuration is incomplete
    ok 18 - fails closed when trusted update signing configuration is incomplete
      ---
      duration_ms: 74.289684
      type: 'test'
      ...
    # Subtest: signs cryptographically bound feeds only in the trusted release phase
    ok 19 - signs cryptographically bound feeds only in the trusted release phase
      ---
      duration_ms: 54.907584
      type: 'test'
      ...
    # Subtest: refuses to sign a renamed extensionless distributable
    ok 20 - refuses to sign a renamed extensionless distributable
      ---
      duration_ms: 45.94176
      type: 'test'
      ...
    # Subtest: refuses to sign when artifact bytes changed after unsigned finalization
    ok 21 - refuses to sign when artifact bytes changed after unsigned finalization
      ---
      duration_ms: 56.985746
      type: 'test'
      ...
    # Subtest: rejects unsigned production metadata and actual signer mismatches
    ok 22 - rejects unsigned production metadata and actual signer mismatches
      ---
      duration_ms: 103.833625
      type: 'test'
      ...
    # Subtest: rejects mixed Windows signers and tampered fingerprint evidence
    ok 23 - rejects mixed Windows signers and tampered fingerprint evidence
      ---
      duration_ms: 59.355253
      type: 'test'
      ...
    # Subtest: parses x64 and arm64 ELF, PE, and Mach-O executable fixtures
    ok 24 - parses x64 and arm64 ELF, PE, and Mach-O executable fixtures
      ---
      duration_ms: 1.215405
      type: 'test'
      ...
    # Subtest: derives Windows target architecture from the full NUPKG independently of its supported bootstrapper
    ok 25 - derives Windows target architecture from the full NUPKG independently of its supported bootstrapper
      ---
      duration_ms: 16.060708
      type: 'test'
      ...
    # Subtest: binds ZIP and NUPKG executables to exact maker-specific canonical paths
    ok 26 - binds ZIP and NUPKG executables to exact maker-specific canonical paths
      ---
      duration_ms: 3.762653
      type: 'test'
      ...
    # Subtest: rejects missing, corrupt, mismatched, and ambiguous packaged Windows authority helpers
    ok 27 - rejects missing, corrupt, mismatched, and ambiguous packaged Windows authority helpers
      ---
      duration_ms: 6.17416
      type: 'test'
      ...
    # Subtest: accepts only the real Forge macOS framework-internal symbolic-link layout
    ok 28 - accepts only the real Forge macOS framework-internal symbolic-link layout
      ---
      duration_ms: 5.055969
      type: 'test'
      ...
    # Subtest: rejects hostile macOS ZIP symbolic links before trusting their payloads
    ok 29 - rejects hostile macOS ZIP symbolic links before trusting their payloads
      ---
      duration_ms: 17.674228
      type: 'test'
      ...
    # Subtest: rejects unsafe, duplicate, shadowed, forged, alternate, and noncanonical archive layouts
    ok 30 - rejects unsafe, duplicate, shadowed, forged, alternate, and noncanonical archive layouts
      ---
      duration_ms: 6.084085
      type: 'test'
      ...
    # Subtest: rejects cross-labeled package architectures at staging and finalization
    ok 31 - rejects cross-labeled package architectures at staging and finalization
      ---
      duration_ms: 50.616851
      type: 'test'
      ...
    1..31
ok 1 - desktop release artifacts
  ---
  duration_ms: 1554.985632
  type: 'suite'
  ...
1..1
# tests 31
# suites 1
# pass 27
# fail 0
# cancelled 0
# skipped 4
# todo 0
# duration_ms 1798.745243

[11/392] apps/desktop/scripts/release-preflight.test.mjs
TAP version 13
# Subtest: desktop release preflight
    # Subtest: accepts only a new immutable tag reachable from protected main and a protected environment
    ok 1 - accepts only a new immutable tag reachable from protected main and a protected environment
      ---
      duration_ms: 3.33884
      type: 'test'
      ...
    # Subtest: accepts an authorization-visible bypass list and fails closed for hidden or denied ruleset details
    ok 2 - accepts an authorization-visible bypass list and fails closed for hidden or denied ruleset details
      ---
      duration_ms: 1.366535
      type: 'test'
      ...
    # Subtest: paginates repository rulesets and reads every full rule definition
    ok 3 - paginates repository rulesets and reads every full rule definition
      ---
      duration_ms: 3.725951
      type: 'test'
      ...
    # Subtest: requires an exact active bypass-free update and deletion tag ruleset
    ok 4 - requires an exact active bypass-free update and deletion tag ruleset
      ---
      duration_ms: 2.851602
      type: 'test'
      ...
    # Subtest: rejects ruleset mutation or deletion during preflight
    ok 5 - rejects ruleset mutation or deletion during preflight
      ---
      duration_ms: 1.89464
      type: 'test'
      ...
    # Subtest: requires the complete effective environment policy set to be exactly desktop-v* tags
    ok 6 - requires the complete effective environment policy set to be exactly desktop-v* tags
      ---
      duration_ms: 4.040367
      type: 'test'
      ...
    # Subtest: requires the separately protected preflight credential environment
    ok 7 - requires the separately protected preflight credential environment
      ---
      duration_ms: 0.924152
      type: 'test'
      ...
    # Subtest: paginates all environment policies and rejects a permissive policy on a later page
    ok 8 - paginates all environment policies and rejects a permissive policy on a later page
      ---
      duration_ms: 0.847219
      type: 'test'
      ...
    # Subtest: rejects missing or ambiguous environment protection and explicit API denial
    ok 9 - rejects missing or ambiguous environment protection and explicit API denial
      ---
      duration_ms: 1.107173
      type: 'test'
      ...
    # Subtest: rejects tags not created by this push, tags off main, and moved or existing releases
    ok 10 - rejects tags not created by this push, tags off main, and moved or existing releases
      ---
      duration_ms: 3.666949
      type: 'test'
      ...
    1..10
ok 1 - desktop release preflight
  ---
  duration_ms: 25.287649
  type: 'suite'
  ...
1..1
# tests 10
# suites 1
# pass 10
# fail 0
# cancelled 0
# skipped 0
# todo 0
# duration_ms 275.734374

[12/392] apps/desktop/scripts/release-publish.test.mjs
TAP version 13
# Subtest: atomic desktop release publication
    # Subtest: creates a draft, verifies the exact profile assets, then publishes
    ok 1 - creates a draft, verifies the exact profile assets, then publishes
      ---
      duration_ms: 69.94062
      type: 'test'
      ...
    # Subtest: leaves a partial upload as a recoverable draft and resumes only matching assets
    ok 2 - leaves a partial upload as a recoverable draft and resumes only matching assets
      ---
      duration_ms: 42.097321
      type: 'test'
      ...
    # Subtest: rejects unexpected, duplicate, size, and content-digest asset mismatches without publishing
    ok 3 - rejects unexpected, duplicate, size, and content-digest asset mismatches without publishing
      ---
      duration_ms: 31.044756
      type: 'test'
      ...
    # Subtest: rejects tag drift before publishing the verified draft
    ok 4 - rejects tag drift before publishing the verified draft
      ---
      duration_ms: 17.214651
      type: 'test'
      ...
    # Subtest: rejects local files outside or missing from finalized checksums
    ok 5 - rejects local files outside or missing from finalized checksums
      ---
      duration_ms: 4.191305
      type: 'test'
      ...
    # Subtest: rejects accidental Windows artifacts in the macOS/Linux manifest before any API call
    ok 6 - rejects accidental Windows artifacts in the macOS/Linux manifest before any API call
      ---
      duration_ms: 4.180088
      type: 'test'
      ...
    1..6
ok 1 - atomic desktop release publication
  ---
  duration_ms: 169.97215
  type: 'suite'
  ...
1..1
# tests 6
# suites 1
# pass 6
# fail 0
# cancelled 0
# skipped 0
# todo 0
# duration_ms 398.262712

[13/392] apps/desktop/scripts/run-bounded-darwin-command.test.mjs
TAP version 13
# Subtest: bounds output while continuously draining both child streams
ok 1 - bounds output while continuously draining both child streams
  ---
  duration_ms: 86.443896
  type: 'test'
  ...
# Subtest: timeout terminates the owned process group including a descendant
ok 2 - timeout terminates the owned process group including a descendant
  ---
  duration_ms: 411.89708
  type: 'test'
  ...
# Subtest: SIGKILL escalation survives leader close and removes a TERM-ignoring descendant
ok 3 - SIGKILL escalation survives leader close and removes a TERM-ignoring descendant
  ---
  duration_ms: 660.087247
  type: 'test'
  ...
# Subtest: timeout remains primary while TERM runs the wrapper cleanup
ok 4 - timeout remains primary while TERM runs the wrapper cleanup
  ---
  duration_ms: 1311.230839
  type: 'test'
  ...
# Subtest: a command failure is not replaced by timeout or cleanup status
ok 5 - a command failure is not replaced by timeout or cleanup status
  ---
  duration_ms: 142.238008
  type: 'test'
  ...
# Subtest: nonzero exit escalates against a TERM-ignoring descendant before releasing the guard
ok 6 - nonzero exit escalates against a TERM-ignoring descendant before releasing the guard
  ---
  duration_ms: 235.486324
  type: 'test'
  ...
# Subtest: rejects executable substitution before creating a child process
ok 7 - rejects executable substitution before creating a child process
  ---
  duration_ms: 0.422628
  type: 'test'
  ...
# Subtest: passes shell metacharacters as one inert argument
ok 8 - passes shell metacharacters as one inert argument
  ---
  duration_ms: 63.727683
  type: 'test'
  ...
# Subtest: CLI command selection ignores PATH and rejects non-allowlisted executables
ok 9 - CLI command selection ignores PATH and rejects non-allowlisted executables
  ---
  duration_ms: 125.229928
  type: 'test'
  ...
# Subtest: CLI timeout diagnostics never echo command arguments or secret values
ok 10 - CLI timeout diagnostics never echo command arguments or secret values
  ---
  duration_ms: 343.949362
  type: 'test'
  ...
1..10
# tests 10
# suites 0
# pass 10
# fail 0
# cancelled 0
# skipped 0
# todo 0
# duration_ms 3605.532323

[14/392] apps/desktop/scripts/sign-darwin-packaged-connect.test.mjs
TAP version 13
# Subtest: Darwin packaged Connect direct signing
    # Subtest: discovers Mach-O files and nested code bundles
    ok 1 - discovers Mach-O files and nested code bundles
      ---
      duration_ms: 12.992699
      type: 'test'
      ...
    # Subtest: selects the exact certificate and signs inside-out with fixed noninteractive options
    ok 2 - selects the exact certificate and signs inside-out with fixed noninteractive options
      ---
      duration_ms: 1.219342
      type: 'test'
      ...
    # Subtest: fails before codesign when the imported certificate is absent, duplicate, or wrong
    ok 3 - fails before codesign when the imported certificate is absent, duplicate, or wrong
      ---
      duration_ms: 1.018487
      type: 'test'
      ...
    # Subtest: emits only fixed classified diagnostics for sensitive native failures
    ok 4 - emits only fixed classified diagnostics for sensitive native failures
      ---
      duration_ms: 1.011374
      type: 'test'
      ...
    1..4
ok 1 - Darwin packaged Connect direct signing
  ---
  duration_ms: 17.290816
  type: 'suite'
  ...
1..1
# tests 4
# suites 1
# pass 4
# fail 0
# cancelled 0
# skipped 0
# todo 0
# duration_ms 244.086339

[15/392] apps/desktop/scripts/verify-darwin-image.test.mjs
TAP version 13
# Subtest: Darwin image verification retries only bounded documented resource states
ok 1 - Darwin image verification retries only bounded documented resource states
  ---
  duration_ms: 13.237553
  type: 'test'
  ...
# Subtest: Darwin image verification does not retry malformed/truncated images or accept mutation
ok 2 - Darwin image verification does not retry malformed/truncated images or accept mutation
  ---
  duration_ms: 16.331703
  type: 'test'
  ...
# Subtest: Darwin image verification holds a fixed hdiutil image behind a real mutation and replacement barrier
ok 3 - Darwin image verification holds a fixed hdiutil image behind a real mutation and replacement barrier # SKIP
  ---
  duration_ms: 0.278049
  type: 'test'
  ...
1..3
# tests 3
# suites 0
# pass 2
# fail 0
# cancelled 0
# skipped 1
# todo 0
# duration_ms 257.658363

[16/392] apps/desktop/scripts/verify-darwin-packaged-connect-signature.test.mjs
TAP version 13
# Subtest: Darwin packaged Connect acceptance signature proof
    # Subtest: uses the portable bounded certificate, display, requirement, and strict proof chain
    ok 1 - uses the portable bounded certificate, display, requirement, and strict proof chain
      ---
      duration_ms: 3.450629
      type: 'test'
      ...
    # Subtest: accepts exactly the generated keychain fingerprint and stable normalized requirements
    ok 2 - accepts exactly the generated keychain fingerprint and stable normalized requirements
      ---
      duration_ms: 12.084176
      type: 'test'
      ...
    # Subtest: rejects duplicate and wrong keychain fingerprints
    ok 3 - rejects duplicate and wrong keychain fingerprints
      ---
      duration_ms: 3.389895
      type: 'test'
      ...
    # Subtest: rejects explicit ad-hoc signature metadata with spacing and case variants
    ok 4 - rejects explicit ad-hoc signature metadata with spacing and case variants
      ---
      duration_ms: 2.13653
      type: 'test'
      ...
    # Subtest: rejects empty signature details
    ok 5 - rejects empty signature details
      ---
      duration_ms: 0.302253
      type: 'test'
      ...
    # Subtest: rejects missing, wrong, duplicate, and conflicting identifier metadata distinctly
    ok 6 - rejects missing, wrong, duplicate, and conflicting identifier metadata distinctly
      ---
      duration_ms: 0.55828
      type: 'test'
      ...
    # Subtest: rejects missing, zero, duplicate, and conflicting signature-size metadata distinctly
    ok 7 - rejects missing, zero, duplicate, and conflicting signature-size metadata distinctly
      ---
      duration_ms: 0.324725
      type: 'test'
      ...
    # Subtest: requires root identifier evidence during both initial and stable native inspections
    ok 8 - requires root identifier evidence during both initial and stable native inspections
      ---
      duration_ms: 7.312463
      type: 'test'
      ...
    # Subtest: requires positive signature-size evidence during both native inspections
    ok 9 - requires positive signature-size evidence during both native inspections
      ---
      duration_ms: 4.099547
      type: 'test'
      ...
    # Subtest: rejects the wrong embedded requirement leaf distinctly
    ok 10 - rejects the wrong embedded requirement leaf distinctly
      ---
      duration_ms: 2.294615
      type: 'test'
      ...
    # Subtest: requires byte-exact embedded designated-requirement stability after reprobe
    ok 11 - requires byte-exact embedded designated-requirement stability after reprobe
      ---
      duration_ms: 2.757097
      type: 'test'
      ...
    # Subtest: strict verification failure has its fixed secret-safe subcode
    ok 12 - strict verification failure has its fixed secret-safe subcode
      ---
      duration_ms: 1.45998
      type: 'test'
      ...
    # Subtest: wraps every native verifier operation in its distinct fixed subcode
    ok 13 - wraps every native verifier operation in its distinct fixed subcode
      ---
      duration_ms: 0.66028
      type: 'test'
      ...
    # Subtest: unsigned code fails at signature display with a fixed secret-safe subcode
    ok 14 - unsigned code fails at signature display with a fixed secret-safe subcode
      ---
      duration_ms: 0.259053
      type: 'test'
      ...
    # Subtest: accepts surrounding requirement metadata and CRLF, spacing, and keyword case variants
    ok 15 - accepts surrounding requirement metadata and CRLF, spacing, and keyword case variants
      ---
      duration_ms: 3.701341
      type: 'test'
      ...
    # Subtest: accepts realistic verbose metadata with exactly one identifier and positive signature size
    ok 16 - accepts realistic verbose metadata with exactly one identifier and positive signature size
      ---
      duration_ms: 0.389345
      type: 'test'
      ...
    # Subtest: rejects duplicate designated lines, wrong identifiers and leaves, and extra clauses
    ok 17 - rejects duplicate designated lines, wrong identifiers and leaves, and extra clauses
      ---
      duration_ms: 0.582846
      type: 'test'
      ...
    # Subtest: ignores unrelated surrounding lines but requires exactly one designated line
    ok 18 - ignores unrelated surrounding lines but requires exactly one designated line
      ---
      duration_ms: 0.281475
      type: 'test'
      ...
    # Subtest: normalizes only surrounding line whitespace when comparing stable requirements
    ok 19 - normalizes only surrounding line whitespace when comparing stable requirements
      ---
      duration_ms: 0.266116
      type: 'test'
      ...
    # Subtest: retains EVIDENCE_ASSERTION_FAILURE for invalid verifier inputs
    ok 20 - retains EVIDENCE_ASSERTION_FAILURE for invalid verifier inputs
      ---
      duration_ms: 0.164347
      type: 'test'
      ...
    1..20
ok 1 - Darwin packaged Connect acceptance signature proof
  ---
  duration_ms: 48.305983
  type: 'suite'
  ...
1..1
# tests 20
# suites 1
# pass 20
# fail 0
# cancelled 0
# skipped 0
# todo 0
# duration_ms 281.540784

[17/392] apps/desktop/scripts/windows-fixture-acl.test.mjs
TAP version 13
# Subtest: keeps the encoded Windows PowerShell 5.1 ACL helper fail-closed and byte-empty
ok 1 - keeps the encoded Windows PowerShell 5.1 ACL helper fail-closed and byte-empty # SKIP
  ---
  duration_ms: 0.677072
  type: 'test'
  ...
1..1
# tests 1
# suites 0
# pass 0
# fail 0
# cancelled 0
# skipped 1
# todo 0
# duration_ms 215.238706

[18/392] apps/desktop/scripts/windows-packaged-connect-staging.test.mjs
TAP version 13
# Subtest: capture redirection mismatch reporting is total and redacted for each launch predicate
ok 1 - capture redirection mismatch reporting is total and redacted for each launch predicate
  ---
  duration_ms: 3.547248
  type: 'test'
  ...
# Subtest: positive host Node producer failures expose only fixed allowlisted evidence
ok 2 - positive host Node producer failures expose only fixed allowlisted evidence
  ---
  duration_ms: 1.002257
  type: 'test'
  ...
# Subtest: hostile diagnostics reject uppercase PATH without matching fixed path subphases
ok 3 - hostile diagnostics reject uppercase PATH without matching fixed path subphases
  ---
  duration_ms: 0.15045
  type: 'test'
  ...
# Subtest: packaged Windows Connect staging contract
    # Subtest: accepts only the exact generated leaf below the fixed canonical staging parent
    ok 1 - accepts only the exact generated leaf below the fixed canonical staging parent
      ---
      duration_ms: 1.286166
      type: 'test'
      ...
    # Subtest: accepts one bounded parent-owned handoff and rejects every other input shape
    ok 2 - accepts one bounded parent-owned handoff and rejects every other input shape
      ---
      duration_ms: 0.56891
      type: 'test'
      ...
    # Subtest: emits only fixed staged-contract predicate evidence
    ok 3 - emits only fixed staged-contract predicate evidence
      ---
      duration_ms: 2.09743
      type: 'test'
      ...
    # Subtest: rejects missing, inaccessible, reparse, wrong-type, and noncanonical entries before preflight
    ok 4 - rejects missing, inaccessible, reparse, wrong-type, and noncanonical entries before preflight
      ---
      duration_ms: 2.574817
      type: 'test'
      ...
    # Subtest: proves target PE architecture and ordinary-user access before returning the executable
    ok 5 - proves target PE architecture and ordinary-user access before returning the executable
      ---
      duration_ms: 1.617824
      type: 'test'
      ...
    # Subtest: maps a hostile preflight callback throw totally and redacts all supplied evidence
    ok 6 - maps a hostile preflight callback throw totally and redacts all supplied evidence
      ---
      duration_ms: 0.981118
      type: 'test'
      ...
    # Subtest: keeps PE type and architecture failures distinct
    ok 7 - keeps PE type and architecture failures distinct
      ---
      duration_ms: 0.808737
      type: 'test'
      ...
    # Subtest: maps hostile exceptions to a fixed path-free allowlist
    ok 8 - maps hostile exceptions to a fixed path-free allowlist
      ---
      duration_ms: 0.672322
      type: 'test'
      ...
    # Subtest: classifies fixed phases without collapsing pre-spawn failures into spawn
    ok 9 - classifies fixed phases without collapsing pre-spawn failures into spawn
      ---
      duration_ms: 0.430071
      type: 'test'
      ...
    # Subtest: maps every preflight transport and exit result to fixed subphase evidence
    ok 10 - maps every preflight transport and exit result to fixed subphase evidence
      ---
      duration_ms: 0.990746
      type: 'test'
      ...
    # Subtest: preflight diagnostics exclude path, SID, account name, stdout, and stderr evidence
    ok 11 - preflight diagnostics exclude path, SID, account name, stdout, and stderr evidence
      ---
      duration_ms: 0.565092
      type: 'test'
      ...
    # Subtest: scopes staged-root and executable leak needles to Windows
    ok 12 - scopes staged-root and executable leak needles to Windows
      ---
      duration_ms: 0.298235
      type: 'test'
      ...
    1..12
ok 4 - packaged Windows Connect staging contract
  ---
  duration_ms: 14.031834
  type: 'suite'
  ...
# Subtest: the workflow stages before alternate credentials and the harness preflights before application spawn
ok 5 - the workflow stages before alternate credentials and the harness preflights before application spawn
  ---
  duration_ms: 28.569182
  type: 'test'
  ...
# Subtest: the PS5.1 child-failure parser accepts only the two exact bounded producer schemas
ok 6 - the PS5.1 child-failure parser accepts only the two exact bounded producer schemas # SKIP
  ---
  duration_ms: 0.119993
  type: 'test'

...(truncated)

View Workflow

… merging.

Implemented the bounded F4 correction without committing or merging.

- Both concurrent joins now preserve the first rejection while draining every started check before cleanup: [agentValidation.ts](/tmp/git-processor/worktrees/integry/propr/pr-2132-followup-2026-09-06T11-36-29/packages/cli/src/commands/agentValidation.ts:473).
- Added staggered subprocess coverage for host/image version and validation checks, verifying cancellation and temporary-resource cleanup wait for the slowest child: [cliAgentValidation.test.ts](/tmp/git-processor/worktrees/integry/propr/pr-2132-followup-2026-09-06T11-36-29/test/cliAgentValidation.test.ts:160).
- Updated the existing test orchestrator stub with its required asynchronous Docker method.

Validation passed:

- Focused cancellation/setup suites: 26 tests
- CLI typecheck
- CLI lint
- CLI build
- `git diff --check`

No visual preview was created because the change is not user-visible.

PR: #2132
Comment by: @integry (ID: 5558903961)
Model: gpt-5.6-sol
@propr-dev

propr-dev Bot commented Sep 6, 2026

Copy link
Copy Markdown
Contributor Author

Applied the requested follow-up changes in commit 8286112

Processed 2 comments:

  • Comment 1 by @integry (ID: 5558951327✓)
  • Comment 2 by @github-actions[bot] (ID: 5558954537✓)

Addressed 1 AI review comment (IDs: 5558948449)

Summary of Changes

Implemented F8 only.

  • Recovery now offers “Review saved choices” for resumable failed, cancelled, and interrupted setups—not only expired credentials.
  • Edited choices are submitted through retry(request).
  • Credential re-entry behavior remains unchanged.
  • Added regression coverage for changing relay configuration to demo mode across all three recovery phases.
  • Captured focused visual preview evidence.

Validation passed:

  • Focused tests: 11/11
  • UI typecheck
  • UI lint with zero warnings
  • git diff --check

Visual preview

Saved-choice recovery action

Saved-choice recovery action

Ordinary relay failure now offers Review saved choices alongside retry in the Linux desktop setup recovery screen.


🤖 Implementation Details

  • Model: GPT-5.6 Sol (high)
  • Requested By: @integry, @github-actions[bot]
  • Time: 10m 18s
  • Tokens: 3,199,116 (3,184,626 in / 14,490 out)
  • Cost: $2.45

Undo ChangesView Task Execution


💡 ProPR Slash Commands
Command Description Example
/merge Merge target branch into this PR and resolve conflicts /merge
/review Request an AI code review /review or /review claude-sonnet
/fix Implement fixes for issues found by /review /fix or /fix address the null check issue
/switch Change the AI model for this PR /switch claude-opus
/use Override the model for a single follow-up run /use claude-sonnet
/ultrafix Loop review→fix cycles until score goal is met /ultrafix or /ultrafix goal=8 max=10

🤖 Generated by ProPR
Processing comment IDs: 5558951327✓, 5558954537✓

Implemented F8 only.

- Recovery now offers “Review saved choices” for resumable failed, cancelled, and interrupted setups—not only expired credentials.
- Edited choices are submitted through `retry(request)`.
- Credential re-entry behavior remains unchanged.
- Added regression coverage for changing relay configuration to demo mode across all three recovery phases.
- Captured focused visual preview evidence.

Validation passed:

- Focused tests: 11/11
- UI typecheck
- UI lint with zero warnings
- `git diff --check`

PR: #2132
Comment by: @integry (ID: 5558951327)
Comment by: @github-actions[bot] (ID: 5558954537)
Model: gpt-5.6-sol
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant