Skip to content

[1990 by GPT-5.6 Sol] Add persisted synthetic-agent and synthetic-model configuration contracts - #1993

Merged
propr-dev[bot] merged 8 commits into
1990-epic-synthetic-pool-eusfrom
1990/gpt-5.6-sol-add-persisted-synthetic-a-20260829-2324-okp
Aug 30, 2026
Merged

[1990 by GPT-5.6 Sol] Add persisted synthetic-agent and synthetic-model configuration contracts#1993
propr-dev[bot] merged 8 commits into
1990-epic-synthetic-pool-eusfrom
1990/gpt-5.6-sol-add-persisted-synthetic-a-20260829-2324-okp

Conversation

@propr-dev

@propr-dev propr-dev Bot commented Aug 29, 2026

Copy link
Copy Markdown
Contributor

AI Implementation Summary

Closes #1990

Branch: 1990/gpt-5.6-sol-add-persisted-synthetic-a-20260829-2324-okp
Commits: ✅ Changes committed (8b456ab)


AI Processing Completed

Execution Details:

Summary:
Implemented GitHub issue #1990.

Key changes:

  • Added shared Zod contracts for synthetic agents, models, members, strategies, priorities, and usage caps in syntheticAgents.ts.
  • Added separate synthetic_agents persistence and core loaders/savers.
  • Added authenticated GET/POST /api/config/synthetic-agents endpoints with actionable validation errors.
  • Enforced direct/synthetic alias uniqueness, model references, duplicate detection, and deletion protection.
  • Added synthetic agents to /api/instance/catalog and retained /api/catalog compatibility.
  • Added optional catalog id and kind discriminators.
  • Added comprehensive schema, persistence, API, catalog, and deletion/disable tests.

Verification passed:

  • Shared, core, API, and root TypeScript checks
  • Core and API linting
  • 107 focused synthetic/configuration tests
  • Full standard unit suite, including existing direct-agent and catalog tests

Detailed Logs:

  • Conversation: 79 messages
  • Session: 01a04fd7-967d-7973-9428-e28c36693c58

Log files stored at:

  • conversation: /tmp/claude-logs/issue-1990-2026-08-29T23-36-26-661Z-conversation.json
  • output: /tmp/claude-logs/issue-1990-2026-08-29T23-36-26-661Z-output.txt
Latest Conversation Messages

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


💡 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!

…hetic-model

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

Implementation completed successfully.
@propr-dev

propr-dev Bot commented Aug 29, 2026

Copy link
Copy Markdown
Contributor Author

/ultrafix
Triggered automatically by Planner execution settings.

@propr-dev propr-dev Bot added the ultrafix label Aug 29, 2026
@propr-dev

propr-dev Bot commented Aug 29, 2026

Copy link
Copy Markdown
Contributor Author

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

First action: /review

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

@github-actions

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/309] packages/api/test/agentLoginRoutes.test.ts
TAP version 13
# Subtest: agent login session manager
    # Subtest: builds an allowlisted Docker login container without forwarding host secrets
    ok 1 - builds an allowlisted Docker login container without forwarding host secrets
      ---
      duration_ms: 2.391017
      type: 'test'
      ...
    # Subtest: maps a ProPR-managed account to the managed host root and marks its container ownership as safe to normalize
    ok 2 - maps a ProPR-managed account to the managed host root and marks its container ownership as safe to normalize
      ---
      duration_ms: 1.065924
      type: 'test'
      ...
    # Subtest: creates a ProPR-managed credential directory before starting Docker
    ok 3 - creates a ProPR-managed credential directory before starting Docker
      ---
      duration_ms: 1.878616
      type: 'test'
      ...
    # Subtest: fails clearly instead of resolving a default home path inside a containerized API
    ok 4 - fails clearly instead of resolving a default home path inside a containerized API
      ---
      duration_ms: 0.505952
      type: 'test'
      ...
    # Subtest: rejects unsafe credential roots and option-like image names
    ok 5 - rejects unsafe credential roots and option-like image names
      ---
      duration_ms: 0.346892
      type: 'test'
      ...
    # Subtest: streams sanitized output, accepts input, and records successful completion
    ok 6 - streams sanitized output, accepts input, and records successful completion
      ---
      duration_ms: 1.318363
      type: 'test'
      ...
    # Subtest: prevents concurrent logins that write the same credential directory
    ok 7 - prevents concurrent logins that write the same credential directory
      ---
      duration_ms: 0.529847
      type: 'test'
      ...
    # Subtest: pulls a missing agent image before creating the login container
    ok 8 - pulls a missing agent image before creating the login container
      ---
      duration_ms: 0.506672
      type: 'test'
      ...
    # Subtest: renews the session deadline when the user sends input
    ok 9 - renews the session deadline when the user sends input
      ---
      duration_ms: 0.511189
      type: 'test'
      ...
    # Subtest: sweeps only orphaned login containers from the current stack scope
    ok 10 - sweeps only orphaned login containers from the current stack scope
      ---
      duration_ms: 0.402385
      type: 'test'
      ...
    1..10
ok 1 - agent login session manager
  ---
  duration_ms: 10.499576
  type: 'suite'
  ...
# Subtest: agent login routes
    # Subtest: starts and returns only the requesting user login session
    ok 1 - starts and returns only the requesting user login session
      ---
      duration_ms: 0.60505
      type: 'test'
      ...
    # Subtest: accepts the same agent alias on start and follow-up calls, including while disabled
    ok 2 - accepts the same agent alias on start and follow-up calls, including while disabled
      ---
      duration_ms: 0.496166
      type: 'test'
      ...
    # Subtest: rejects interactive login for an unsupported agent type
    ok 3 - rejects interactive login for an unsupported agent type
      ---
      duration_ms: 0.40542
      type: 'test'
      ...
    1..3
ok 2 - agent login routes
  ---
  duration_ms: 1.621219
  type: 'suite'
  ...
# [2026-08-29 23:37:36.020 +0000] �[32mINFO�[39m: �[36mSQLite database connection established successfully�[39m
#     filename: "/tmp/propr-test-suite-hz71GE/001-agentLoginRoutes.test.ts/propr.test.sqlite"
#     environment: "test"
# [2026-08-29 23:37:36.029 +0000] �[32mINFO�[39m: �[36mSQLite database connection closed�[39m
# [2026-08-29 23:37:36.029 +0000] �[32mINFO�[39m: �[36mShutting down queue...�[39m
# [2026-08-29 23:37:36.029 +0000] �[32mINFO�[39m: �[36mQueue was never initialized, nothing to shutdown�[39m
1..2
# tests 13
# suites 2
# pass 13
# fail 0
# cancelled 0
# skipped 0
# todo 0
# duration_ms 2002.7721

[2/309] packages/api/test/agentRuntimeRoutes.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: 3.323651
      type: 'test'
      ...
    # Subtest: persists a failed state when queue submission fails
    ok 2 - persists a failed state when queue submission fails
      ---
      duration_ms: 0.72442
      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: 0.614795
      type: 'test'
      ...
    # Subtest: times out slow runtime package validation before queueing
    ok 4 - times out slow runtime package validation before queueing
      ---
      duration_ms: 5.21822
      type: 'test'
      ...
    # Subtest: returns package suggestions from configured runtimes
    ok 5 - returns package suggestions from configured runtimes
      ---
      duration_ms: 0.661075
      type: 'test'
      ...
    # Subtest: enforces PROPR_ADMIN_USERS when configured
    ok 6 - enforces PROPR_ADMIN_USERS when configured
      ---
      duration_ms: 0.282185
      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.27898
      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.294253
      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.384518
      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: 2.144216
      type: 'test'
      ...
    # Subtest: resolves the runtime build queue lazily when queueing
    ok 11 - resolves the runtime build queue lazily when queueing
      ---
      duration_ms: 0.675907
      type: 'test'
      ...
    # Subtest: reports apply load failures through the route response
    ok 12 - reports apply load failures through the route response
      ---
      duration_ms: 0.358619
      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.427453
      type: 'test'
      ...
    # Subtest: rejects runtime verification without runtime-management permission
    ok 14 - rejects runtime verification without runtime-management permission
      ---
      duration_ms: 0.200982
      type: 'test'
      ...
    1..14
ok 1 - agent runtime package routes
  ---
  duration_ms: 16.841938
  type: 'suite'
  ...
# [2026-08-29 23:37:37.839 +0000] �[32mINFO�[39m: �[36mSQLite database connection established successfully�[39m
#     filename: "/tmp/propr-test-suite-hz71GE/002-agentRuntimeRoutes.test.ts/propr.test.sqlite"
#     environment: "test"
# [2026-08-29 23:37:37.854 +0000] �[32mINFO�[39m: �[36mSQLite database connection closed�[39m
1..1
# tests 14
# suites 1
# pass 14
# fail 0
# cancelled 0
# skipped 0
# todo 0
# duration_ms 1715.43822

[3/309] packages/api/test/attachmentUploadCleanup.test.ts
TAP version 13
# Subtest: upload handler removes Multer files rejected before attachment processing
ok 1 - upload handler removes Multer files rejected before attachment processing
  ---
  duration_ms: 8.674811
  type: 'test'
  ...
# Subtest: attachment processing removes temporary and final files when persistence fails
ok 2 - attachment processing removes temporary and final files when persistence fails
  ---
  duration_ms: 4.124149
  type: 'test'
  ...
# Subtest: temporary cleanup refuses paths outside its configured root
ok 3 - temporary cleanup refuses paths outside its configured root
  ---
  duration_ms: 0.527534
  type: 'test'
  ...
# Subtest: temporary cleanup rejects dot-segment paths that resolve above its root
ok 4 - temporary cleanup rejects dot-segment paths that resolve above its root
  ---
  duration_ms: 0.410077
  type: 'test'
  ...
# Subtest: attachment processing rejects path-like draft IDs and still cleans its temp file
ok 5 - attachment processing rejects path-like draft IDs and still cleans its temp file
  ---
  duration_ms: 1.421599
  type: 'test'
  ...
# [2026-08-29 23:37:39.690 +0000] �[32mINFO�[39m: �[36mSQLite database connection established successfully�[39m
#     filename: "/tmp/propr-test-suite-hz71GE/003-attachmentUploadCleanup.test.ts/propr.test.sqlite"
#     environment: "test"
# [2026-08-29 23:37:39.705 +0000] �[32mINFO�[39m: �[36mSQLite database connection closed�[39m
1..5
# tests 5
# suites 0
# pass 5
# fail 0
# cancelled 0
# skipped 0
# todo 0
# duration_ms 1747.384677

[4/309] packages/api/test/authGithubTokens.test.ts
TAP version 13
# Refreshing GitHub token for user octocat (force=true)
# Successfully refreshed GitHub token for user octocat
# Refreshing GitHub token for user octocat (force=true)
# GitHub token refresh failed with status 503
# Refreshing GitHub token for user octocat (force=true)
# Subtest: isGitHubTokenExpired handles missing, future, exact, and past expiry values
ok 1 - isGitHubTokenExpired handles missing, future, exact, and past expiry values
  ---
  duration_ms: 1.939257
  type: 'test'
  ...
# Subtest: ensureAuthenticated refreshes an expired GitHub token before continuing
ok 2 - ensureAuthenticated refreshes an expired GitHub token before continuing
  ---
  duration_ms: 4.02211
  type: 'test'
  ...
# Subtest: ensureAuthenticated reports a temporary error when refresh fails recoverably
ok 3 - ensureAuthenticated reports a temporary error when refresh fails recoverably
  ---
  duration_ms: 1.044742
  type: 'test'
  ...
# Successfully refreshed GitHub token for user octocat
# Saved refreshed GitHub token for concurrent request by user octocat
# Refreshing GitHub token for user octocat (force=true)
# GitHub token refresh response missing access_token
# Refreshing GitHub token for user octocat (force=true)
# GitHub token refresh error: bad_refresh_token - The refresh token is invalid.
# Marked GitHub OAuth session for user octocat as requiring re-authentication (bad_refresh_token)
# GitHub token expired or revoked, attempting token refresh
# Refreshing GitHub token for user octocat (force=true)
# Successfully refreshed GitHub token for user octocat
# Token refresh successful, client should retry
# Subtest: ensureAuthenticated coalesces concurrent expired-token refreshes for one session
ok 4 - ensureAuthenticated coalesces concurrent expired-token refreshes for one session
  ---
  duration_ms: 11.290964
  type: 'test'
  ...
# Subtest: ensureAuthenticated rejects an expired GitHub token with no refresh token
ok 5 - ensureAuthenticated rejects an expired GitHub token with no refresh token
  ---
  duration_ms: 0.59816
  type: 'test'
  ...
# Subtest: ensureAuthenticated reports a temporary error when refresh omits an access token
ok 6 - ensureAuthenticated reports a temporary error when refresh omits an access token
  ---
  duration_ms: 0.681205
  type: 'test'
  ...
# Subtest: ensureAuthenticated clears the session after an unrecoverable refresh error
ok 7 - ensureAuthenticated clears the session after an unrecoverable refresh error
  ---
  duration_ms: 0.795797
  type: 'test'
  ...
# Subtest: GitHub route auth error emits TOKEN_REFRESHED after a successful refresh
ok 8 - GitHub route auth error emits TOKEN_REFRESHED after a successful refresh
  ---
  duration_ms: 0.743518
  type: 'test'
  ...
# [2026-08-29 23:37:41.921 +0000] �[32mINFO�[39m: �[36mSQLite database connection established successfully�[39m
#     filename: "/tmp/propr-test-suite-hz71GE/004-authGithubTokens.test.ts/propr.test.sqlite"
#     environment: "test"
# [2026-08-29 23:37:41.938 +0000] �[32mINFO�[39m: �[36mSQLite database connection closed�[39m
1..8
# tests 8
# suites 0
# pass 8
# fail 0
# cancelled 0
# skipped 0
# todo 0
# duration_ms 2370.096946

[5/309] packages/api/test/authRedirect.test.ts
TAP version 13
# Subtest: auth redirect allowlist treats FRONTEND_URL as exact host only
ok 1 - auth redirect allowlist treats FRONTEND_URL as exact host only
  ---
  duration_ms: 36.041661
  type: 'test'
  ...
# Subtest: proxy mode auth redirect fallback returns the hosted UI origin
ok 2 - proxy mode auth redirect fallback returns the hosted UI origin
  ---
  duration_ms: 7.852221
  type: 'test'
  ...
# Subtest: auth redirect allowlist permits subdomains only for explicit wildcard-style hosts
ok 3 - auth redirect allowlist permits subdomains only for explicit wildcard-style hosts
  ---
  duration_ms: 4.601511
  type: 'test'
  ...
# Subtest: auth redirect allowlist permits leading-dot COOKIE_DOMAIN subdomains
ok 4 - auth redirect allowlist permits leading-dot COOKIE_DOMAIN subdomains
  ---
  duration_ms: 3.78936
  type: 'test'
  ...
# Subtest: auth redirect allowlist permits exact additional hosts without permitting their subdomains
ok 5 - auth redirect allowlist permits exact additional hosts without permitting their subdomains
  ---
  duration_ms: 5.73806
  type: 'test'
  ...
# Subtest: auth redirect allowlist permits wildcard entries with protocol prefixes
ok 6 - auth redirect allowlist permits wildcard entries with protocol prefixes
  ---
  duration_ms: 4.816054
  type: 'test'
  ...
# Subtest: auth redirect allowlist rejects invalid URLs and non-http protocols
ok 7 - auth redirect allowlist rejects invalid URLs and non-http protocols
  ---
  duration_ms: 8.554838
  type: 'test'
  ...
# Subtest: auth redirect allowlist only permits cleartext HTTP for localhost
ok 8 - auth redirect allowlist only permits cleartext HTTP for localhost
  ---
  duration_ms: 4.303423
  type: 'test'
  ...
# Subtest: auth redirect allowlist permits configured local IP literals
ok 9 - auth redirect allowlist permits configured local IP literals
  ---
  duration_ms: 4.432507
  type: 'test'
  ...
# Subtest: auth redirect allowlist ignores malformed additional host entries
ok 10 - auth redirect allowlist ignores malformed additional host entries
  ---
  duration_ms: 3.997783
  type: 'test'
  ...
# [2026-08-29 23:37:44.339 +0000] �[32mINFO�[39m: �[36mSQLite database connection established successfully�[39m
#     filename: "/tmp/propr-test-suite-hz71GE/005-authRedirect.test.ts/propr.test.sqlite"
#     environment: "test"
# [2026-08-29 23:37:44.416 +0000] �[32mINFO�[39m: �[36mSQLite database connection closed�[39m
1..10
# tests 10
# suites 0
# pass 10
# fail 0
# cancelled 0
# skipped 0
# todo 0
# duration_ms 2437.744062

[6/309] packages/api/test/configAgentValidation.antigravity.test.ts
TAP version 13
# Subtest: agent config validation accepts antigravity and rejects gemini for new configs
ok 1 - agent config validation accepts antigravity and rejects gemini for new configs
  ---
  duration_ms: 1230.885641
  type: 'test'
  ...
# [2026-08-29 23:37:46.545 +0000] �[32mINFO�[39m: �[36mSQLite database connection established successfully�[39m
#     filename: "/tmp/propr-test-suite-hz71GE/006-configAgentValidation.antigravity.test.ts/propr.test.sqlite"
#     environment: "test"
1..1
# tests 1
# suites 0
# pass 1
# fail 0
# cancelled 0
# skipped 0
# todo 0
# duration_ms 1745.320227

[7/309] packages/api/test/configReloadSubscription.test.ts
TAP version 13
# Subtest: API config subscription serializes the startup reload with settings updates
ok 1 - API config subscription serializes the startup reload with settings updates
  ---
  duration_ms: 3.78966
  type: 'test'
  ...
# Subtest: API config subscription reloads repository updates
ok 2 - API config subscription reloads repository updates
  ---
  duration_ms: 0.361914
  type: 'test'
  ...
# Subtest: API config subscription ignores unrelated and malformed events
ok 3 - API config subscription ignores unrelated and malformed events
  ---
  duration_ms: 0.255755
  type: 'test'
  ...
# Subtest: a failed API settings reload does not block the next notification
ok 4 - a failed API settings reload does not block the next notification
  ---
  duration_ms: 0.299631
  type: 'test'
  ...
1..4
# tests 4
# suites 0
# pass 4
# fail 0
# cancelled 0
# skipped 0
# todo 0
# duration_ms 196.720231

[8/309] packages/api/test/configRoutesIndexingSummarization.test.ts
TAP version 13
# [2026-08-29 23:37:48.713 +0000] �[32mINFO�[39m: �[36mSQLite database connection established successfully�[39m
#     filename: "/tmp/propr-test-suite-hz71GE/008-configRoutesIndexingSummarization.test.ts/propr.test.sqlite"
#     environment: "test"
# [2026-08-29 23:37:48.740 +0000] �[32mINFO�[39m: �[36mRunning database migrations...�[39m
# Subtest: postSummarizationSettings trims model-specific aliases before saving
ok 1 - postSummarizationSettings trims model-specific aliases before saving
  ---
  duration_ms: 1508.857503
  type: 'test'
  ...
# Subtest: postSummarizationSettings rejects enabled summarization without a primary alias
ok 2 - postSummarizationSettings rejects enabled summarization without a primary alias
  ---
  duration_ms: 2.880894
  type: 'test'
  ...
# [2026-08-29 23:37:48.937 +0000] �[32mINFO�[39m: �[36mDatabase migrations completed successfully�[39m
# [2026-08-29 23:37:48.938 +0000] �[32mINFO�[39m: �[36mSuccessfully loaded agents configuration�[39m
#     agentCount: 2
# [2026-08-29 23:37:48.938 +0000] �[32mINFO�[39m: �[36mSuccessfully loaded summarization settings�[39m
#     summarization: {
#       "enabled": false,
#       "agent_alias": "",
#       "fallback_agent_alias": "",
#       "custom_prompt": ""
#     }
# [2026-08-29 23:37:48.940 +0000] �[32mINFO�[39m: �[36mSuccessfully saved summarization settings�[39m
#     summarization: {
#       "enabled": true,
#       "agent_alias": "codex:gpt-5.5",
#       "fallback_agent_alias": "fallback:gpt-5.4",
#       "custom_prompt": ""
#     }
# [2026-08-29 23:37:48.940 +0000] �[32mINFO�[39m: �[36mSuccessfully loaded summarization settings�[39m
#     summarization: {
#       "enabled": true,
#       "agent_alias": "codex:gpt-5.5",
#       "fallback_agent_alias": "fallback:gpt-5.4",
#       "custom_prompt": ""
#     }
# [2026-08-29 23:37:48.967 +0000] �[32mINFO�[39m: �[36mSQLite database connection closed�[39m
1..2
# tests 2
# suites 0
# pass 2
# fail 0
# cancelled 0
# skipped 0
# todo 0
# duration_ms 1754.711989

[9/309] packages/api/test/connectAuth.test.ts
TAP version 13
# Subtest: relay tunnel mode uses Connect without local OAuth credentials
ok 1 - relay tunnel mode uses Connect without local OAuth credentials
  ---
  duration_ms: 0.870249
  type: 'test'
  ...
# Subtest: relay tunnel mode preserves Connect for operator-supplied service endpoints
ok 2 - relay tunnel mode preserves Connect for operator-supplied service endpoints
  ---
  duration_ms: 0.161453
  type: 'test'
  ...
# Subtest: local relay mode uses Connect without a per-instance OAuth App
ok 3 - local relay mode uses Connect without a per-instance OAuth App
  ---
  duration_ms: 0.200722
  type: 'test'
  ...
# Subtest: off-tunnel relay inference rejects callbacks outside the exact loopback allowlist
ok 4 - off-tunnel relay inference rejects callbacks outside the exact loopback allowlist
  ---
  duration_ms: 0.22572
  type: 'test'
  ...
# Subtest: off-tunnel custom relay enrollment does not infer hosted Connect auth
ok 5 - off-tunnel custom relay enrollment does not infer hosted Connect auth
  ---
  duration_ms: 0.101142
  type: 'test'
  ...
# Subtest: literal example OAuth placeholders do not enable GitHub web auth
ok 6 - literal example OAuth placeholders do not enable GitHub web auth
  ---
  duration_ms: 0.088293
  type: 'test'
  ...
# Subtest: explicit custom GitHub web auth remains supported
ok 7 - explicit custom GitHub web auth remains supported
  ---
  duration_ms: 0.114942
  type: 'test'
  ...
# Subtest: explicit custom GitHub web auth wins over relay inference off-tunnel
ok 8 - explicit custom GitHub web auth wins over relay inference off-tunnel
  ---
  duration_ms: 0.112009
  type: 'test'
  ...
# Subtest: Connect authorization URL carries the exact callback and CSRF state
ok 9 - Connect authorization URL carries the exact callback and CSRF state
  ---
  duration_ms: 0.393171
  type: 'test'
  ...
# Subtest: redeems a Connect code server-to-server without exposing the relay token in the body
ok 10 - redeems a Connect code server-to-server without exposing the relay token in the body
  ---
  duration_ms: 16.800514
  type: 'test'
  ...
# Subtest: binds the Connect identity username to the validated token owner
ok 11 - binds the Connect identity username to the validated token owner
  ---
  duration_ms: 0.571009
  type: 'test'
  ...
1..11
# tests 11
# suites 0
# pass 11
# fail 0
# cancelled 0
# skipped 0
# todo 0
# duration_ms 331.183409

[10/309] packages/api/test/connectAuthCallback.test.ts
TAP version 13
# [2026-08-29 23:37:51.346 +0000] �[32mINFO�[39m: �[36mSQLite database connection established successfully�[39m
#     filename: "/tmp/propr-test-suite-hz71GE/010-connectAuthCallback.test.ts/propr.test.sqlite"
#     environment: "test"
# Subtest: Connect login preserves a validated redirect_to across Passport session regeneration
ok 1 - Connect login preserves a validated redirect_to across Passport session regeneration
  ---
  duration_ms: 3034.197512
  type: 'test'
  ...
# [2026-08-29 23:37:54.377 +0000] �[32mINFO�[39m: �[36mSQLite database connection closed�[39m
1..1
# tests 1
# suites 0
# pass 1
# fail 0
# cancelled 0
# skipped 0
# todo 0
# duration_ms 5267.489518

[11/309] packages/api/test/corsValidation.test.ts
TAP version 13
# Subtest: CORS allows the hosted UI origin under proxy mode
ok 1 - CORS allows the hosted UI origin under proxy mode
  ---
  duration_ms: 0.843789
  type: 'test'
  ...
# Subtest: CORS rejects unrelated origins under proxy mode
ok 2 - CORS rejects unrelated origins under proxy mode
  ---
  duration_ms: 0.21947
  type: 'test'
  ...
# Subtest: CORS allows requests with no origin
ok 3 - CORS allows requests with no origin
  ---
  duration_ms: 0.10618
  type: 'test'
  ...
# Subtest: CORS allows localhost for development
ok 4 - CORS allows localhost for development
  ---
  duration_ms: 0.112679
  type: 'test'
  ...
# Subtest: CORS rejects non-http(s) localhost schemes
ok 5 - CORS rejects non-http(s) localhost schemes
  ---
  duration_ms: 0.15996
  type: 'test'
  ...
# Subtest: CORS allows COOKIE_DOMAIN subdomains for preview environments
ok 6 - CORS allows COOKIE_DOMAIN subdomains for preview environments
  ---
  duration_ms: 0.124157
  type: 'test'
  ...
# Subtest: CORS preserves http COOKIE_DOMAIN preview compatibility
ok 7 - CORS preserves http COOKIE_DOMAIN preview compatibility
  ---
  duration_ms: 0.140161
  type: 'test'
  ...
# Subtest: CORS validator factory throws on an invalid FRONTEND_URL
ok 8 - CORS validator factory throws on an invalid FRONTEND_URL
  ---
  duration_ms: 0.205339
  type: 'test'
  ...
# Subtest: Express CORS boundary is sanitized in development mode
ok 9 - Express CORS boundary is sanitized in development mode
  ---
  duration_ms: 63.517874
  type: 'test'
  ...
# Subtest: Express CORS boundary is sanitized in production mode
ok 10 - Express CORS boundary is sanitized in production mode
  ---
  duration_ms: 23.476928
  type: 'test'
  ...
1..10
# tests 10
# suites 0
# pass 10
# fail 0
# cancelled 0
# skipped 0
# todo 0
# duration_ms 372.493478

[12/309] packages/api/test/demoMode.test.ts
TAP version 13
# Subtest: demoModeReadOnlyMiddleware rejects mutating requests in demo mode
ok 1 - demoModeReadOnlyMiddleware rejects mutating requests in demo mode
  ---
  duration_ms: 4.740691
  type: 'test'
  ...
# Subtest: demoModeReadOnlyMiddleware blocks auth metadata mutations
ok 2 - demoModeReadOnlyMiddleware blocks auth metadata mutations
  ---
  duration_ms: 1.468379
  type: 'test'
  ...
# Subtest: isDemoMode accepts common truthy environment values
ok 3 - isDemoMode accepts common truthy environment values
  ---
  duration_ms: 0.799703
  type: 'test'
  ...
# Subtest: configured demo mode keeps auth and middleware on the same startup value
ok 4 - configured demo mode keeps auth and middleware on the same startup value
  ---
  duration_ms: 1.016689
  type: 'test'
  ...
# Subtest: demo Redis facade covers read-only route Redis usage
ok 5 - demo Redis facade covers read-only route Redis usage
  ---
  duration_ms: 2.584447
  type: 'test'
  ...
# Subtest: demo Express GET routes work with the in-memory Redis facade
ok 6 - demo Express GET routes work with the in-memory Redis facade
  ---
  duration_ms: 42.898151
  type: 'test'
  ...
# Subtest: ensureAuthenticated attaches the synthetic demo user
ok 7 - ensureAuthenticated attaches the synthetic demo user
  ---
  duration_ms: 0.847655
  type: 'test'
  ...
# Subtest: ensureAuthenticated ignores bearer auth and attaches the synthetic demo user in demo mode
ok 8 - ensureAuthenticated ignores bearer auth and attaches the synthetic demo user in demo mode
  ---
  duration_ms: 0.669186
  type: 'test'
  ...
# Subtest: demo repository metadata resolves enabled configured repositories
ok 9 - demo repository metadata resolves enabled configured repositories
  ---
  duration_ms: 7.195485
  type: 'test'
  ...
# Subtest: repository config branch validation documents ProPR-supported branch names
ok 10 - repository config branch validation documents ProPR-supported branch names
  ---
  duration_ms: 1.812847
  type: 'test'
  ...
# Subtest: /api/github/repos returns configured and persisted repositories in demo mode
ok 11 - /api/github/repos returns configured and persisted repositories in demo mode
  ---
  duration_ms: 196.717572
  type: 'test'
  ...
# Subtest: demo repository metadata resolves persisted repositories without configured allowlists
ok 12 - demo repository metadata resolves persisted repositories without configured allowlists
  ---
  duration_ms: 7.858812
  type: 'test'
  ...
# Subtest: demo repository metadata ignores malformed database repository names
ok 13 - demo repository metadata ignores malformed database repository names
  ---
  duration_ms: 1.254497
  type: 'test'
  ...
# Subtest: planner demo reads use the curated database without owner or repository allowlists
ok 14 - planner demo reads use the curated database without owner or repository allowlists
  ---
  duration_ms: 6.032325
  type: 'test'
  ...
# Subtest: repo todo demo reads use the curated database without owner filters
ok 15 - repo todo demo reads use the curated database without owner filters
  ---
  duration_ms: 4.688002
  type: 'test'
  ...
# Subtest: auth demo-mode metadata endpoint reports startup environment value
ok 16 - auth demo-mode metadata endpoint reports startup environment value
  ---
  duration_ms: 7.648816
  type: 'test'
  ...
# [2026-08-29 23:37:57.270 +0000] �[32mINFO�[39m: �[36mSQLite database connection established successfully�[39m
#     filename: "/tmp/propr-test-suite-hz71GE/012-demoMode.test.ts/propr.test.sqlite"
#     environment: "test"
# [2026-08-29 23:37:57.525 +0000] �[32mINFO�[39m: �[36mSuccessfully saved monitored repositories�[39m
#     repos: [
#       {
#         "id": "1",
#         "name": "integry/propr",
#         "enabled": true
#       },
#       {
#         "id": "2",
#         "name": "integry/private",
#         "enabled": true
#       },
#       {
#         "id": "3",
#         "name": "integry/disabled",
#         "enabled": false
#       }
#     ]
# [2026-08-29 23:37:57.526 +0000] �[32mINFO�[39m: �[36mSuccessfully loaded all monitored repositories�[39m
#     total_repos: 3
# [2026-08-29 23:37:57.534 +0000] �[32mINFO�[39m: �[36mSuccessfully loaded all monitored repositories�[39m
#     total_repos: 0
# [2026-08-29 23:37:57.542 +0000] �[32mINFO�[39m: �[36mSuccessfully saved monitored repositories�[39m
#     repos: [
#       {
#         "id": "1",
#         "name": "integry/propr",
#         "enabled": true
#       },
#       {
#         "id": "2",
#         "name": "integry/private",
#         "enabled": true
#       }
#     ]
1..16
# tests 16
# suites 0
# pass 16
# fail 0
# cancelled 0
# skipped 0
# todo 0
# duration_ms 2606.144941

[13/309] packages/api/test/dockerCommandSafety.test.ts
TAP version 13
# Subtest: accepts Docker IDs and ProPR-generated container names
ok 1 - accepts Docker IDs and ProPR-generated container names
  ---
  duration_ms: 0.647133
  type: 'test'
  ...
# Subtest: rejects container references that could be parsed as options or shell syntax
ok 2 - rejects container references that could be parsed as options or shell syntax
  ---
  duration_ms: 0.123356
  type: 'test'
  ...
# Subtest: production subprocess call sites do not invoke a command shell
ok 3 - production subprocess call sites do not invoke a command shell
  ---
  duration_ms: 56.782378
  type: 'test'
  ...
# Subtest: no-shell guard rejects aliases and enabled shell options
ok 4 - no-shell guard rejects aliases and enabled shell options
  ---
  duration_ms: 3.86847
  type: 'test'
  ...
1..4
# tests 4
# suites 0
# pass 4
# fail 0
# cancelled 0
# skipped 0
# todo 0
# duration_ms 744.064581

[14/309] packages/api/test/instanceAuthorization.test.ts
TAP version 13
# 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: 8.540418
      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: 3.678814
      type: 'test'
      ...
    # Subtest: grants full admin permissions to PROPR_ADMIN_USERS
    ok 3 - grants full admin permissions to PROPR_ADMIN_USERS
      ---
      duration_ms: 1.935812
      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: 2.060619
      type: 'test'
      ...
    # Subtest: resolves demo users without installation permissions
    ok 5 - resolves demo users without installation permissions
      ---
      duration_ms: 1.687589
      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: 1.636361
      type: 'test'
      ...
    # Subtest: current-user responses expose capabilities without OAuth credentials
    ok 7 - current-user responses expose capabilities without OAuth credentials
      ---
      duration_ms: 1.988191
      type: 'test'
      ...
    1..7
ok 1 - instance authorization
  ---
  duration_ms: 22.531754
  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: 2.450056
      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: 4.498707
      type: 'test'
      ...
    # Subtest: adds explicit members and writes an audit entry
    ok 3 - adds explicit members and writes an audit entry
      ---
      duration_ms: 3.639575
      type: 'test'
      ...
    # Subtest: keeps environment authority separate from durable role assignments
    ok 4 - keeps environment authority separate from durable role assignments
      ---
      duration_ms: 1.906057
      type: 'test'
      ...
    # Subtest: prevents removing the last durable administrator
    ok 5 - prevents removing the last durable administrator
      ---
      duration_ms: 3.33021
      type: 'test'
      ...
    # Subtest: allows role changes once another administrator exists
    ok 6 - allows role changes once another administrator exists
      ---
      duration_ms: 4.301461
      type: 'test'
      ...
    1..6
ok 2 - instance member service
  ---
  duration_ms: 20.436413
  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: 1.641749
      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: 1.282048
      type: 'test'
      ...
    1..2
ok 3 - instance catalog
  ---
  duration_ms: 2.999843
  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: 2.061812
      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: 1.046114
      type: 'test'
      ...
    # Subtest: rejects GitHub usernames with trailing or consecutive hyphens
    ok 3 - rejects GitHub usernames with trailing or consecutive hyphens
      ---
      duration_ms: 1.168588
      type: 'test'
      ...
    1..3
ok 4 - instance admin routes
  ---
  duration_ms: 4.386409
  type: 'suite'
  ...
# [2026-08-29 23:38:00.387 +0000] �[32mINFO�[39m: �[36mSQLite database connection established successfully�[39m
#     filename: "/tmp/propr-test-suite-hz71GE/014-instanceAuthorization.test.ts/propr.test.sqlite"
#     environment: "test"
# [2026-08-29 23:38:00.433 +0000] �[32mINFO�[39m: �[36mSQLite database connection closed�[39m
1..4
# tests 18
# suites 4
# pass 18
# fail 0
# cancelled 0
# skipped 0
# todo 0
# duration_ms 1516.805878

[15/309] packages/api/test/listenAddress.test.ts
TAP version 13
# Subtest: direct API execution binds to loopback by default
ok 1 - direct API execution binds to loopback by default
  ---
  duration_ms: 0.649507
  type: 'test'
  ...
# Subtest: containerized API execution remains reachable through its published port
ok 2 - containerized API execution remains reachable through its published port
  ---
  duration_ms: 0.118368
  type: 'test'
  ...
# Subtest: an explicit API listen host overrides environment defaults
ok 3 - an explicit API listen host overrides environment defaults
  ---
  duration_ms: 0.112138
  type: 'test'
  ...
1..3
# tests 3
# suites 0
# pass 3
# fail 0
# cancelled 0
# skipped 0
# todo 0
# duration_ms 205.959315

[16/309] packages/api/test/liveDetailsAntigravity.test.ts
TAP version 13
# Failed to read GitHub App private key: ENOENT: no such file or directory, open '/tmp/missing-key.pem'
# Ensure GH_PRIVATE_KEY_PATH is set correctly in your .env file and points to a valid private key file.
# Subtest: stored output detection recognizes Antigravity stream JSON
ok 1 - stored output detection recognizes Antigravity stream JSON
  ---
  duration_ms: 1.687338
  type: 'test'
  ...
# Subtest: stored output detection keeps Codex message JSONL classified as Codex
ok 2 - stored output detection keeps Codex message JSONL classified as Codex
  ---
  duration_ms: 1.040396
  type: 'test'
  ...
# Subtest: stored output detection keeps Codex result JSONL classified as Codex
ok 3 - stored output detection keeps Codex result JSONL classified as Codex
  ---
  duration_ms: 0.673463
  type: 'test'
  ...
# Subtest: stored output detection keeps Codex tool result JSONL classified as Codex
ok 4 - stored output detection keeps Codex tool result JSONL classified as Codex
  ---
  duration_ms: 0.157707
  type: 'test'
  ...
# Subtest: stored output detection does not treat generic init JSONL as Antigravity
ok 5 - stored output detection does not treat generic init JSONL as Antigravity
  ---
  duration_ms: 0.221623
  type: 'test'
  ...
# Subtest: stored output detection recognizes Antigravity JSONL from result stats when init is missing
ok 6 - stored output detection recognizes Antigravity JSONL from result stats when init is missing
  ---
  duration_ms: 0.27291
  type: 'test'
  ...
# Subtest: stored output detection keeps generic result token stats classified as Codex
ok 7 - stored output detection keeps generic result token stats classified as Codex
  ---
  duration_ms: 0.143716
  type: 'test'
  ...
# Subtest: stored output detection recognizes truncated Antigravity result JSON
ok 8 - stored output detection recognizes truncated Antigravity result JSON
  ---
  duration_ms: 0.117447
  type: 'test'
  ...
# Subtest: stored output detection recognizes Antigravity message JSON with model metadata
ok 9 - stored output detection recognizes Antigravity message JSON with model metadata
  ---
  duration_ms: 0.267402
  type: 'test'
  ...
# Subtest: stored output parsing renders only Antigravity analysis events through live details
ok 10 - stored output parsing renders only Antigravity analysis events through live details
  ---
  duration_ms: 67.283054
  type: 'test'
  ...
# Subtest: Antigravity 1.1.12 stream text remains visible through live details
ok 11 - Antigravity 1.1.12 stream text remains visible through live details
  ---
  duration_ms: 1.685946
  type: 'test'
  ...
# Subtest: stored output detection and live-details rendering consume Antigravity stream arrays
ok 12 - stored output detection and live-details rendering consume Antigravity stream arrays
  ---
  duration_ms: 1.317862
  type: 'test'
  ...
# Subtest: Antigravity cache-only stream usage remains visible through live details
ok 13 - Antigravity cache-only stream usage remains visible through live details
  ---
  duration_ms: 2.416165
  type: 'test'
  ...
# Subtest: stored output parsing filters Antigravity transcript tool items
ok 14 - stored output parsing filters Antigravity transcript tool items
  ---
  duration_ms: 0.948337
  type: 'test'
  ...
# Subtest: Claude-format Antigravity conversation files render only planner analysis
ok 15 - Claude-format Antigravity conversation files render only planner analysis
  ---
  duration_ms: 1.058092
  type: 'test'
  ...
# Subtest: execution detail fallback filters Antigravity transcript tool rows
ok 16 - execution detail fallback filters Antigravity transcript tool rows
  ---
  duration_ms: 0.807064
  type: 'test'
  ...
# [2026-08-29 23:38:02.310 +0000] �[32mINFO�[39m: �[36mSQLite database connection established successfully�[39m
#     filename: "/tmp/propr-test-suite-hz71GE/016-liveDetailsAntigravity.test.ts/propr.test.sqlite"
#     environment: "test"
1..16
# tests 16
# suites 0
# pass 16
# fail 0
# cancelled 0
# skipped 0
# todo 0
# duration_ms 1692.56352

[17/309] packages/api/test/liveDetailsCodexDbFallback.test.ts
TAP version 13
# [live-details] jobId: integry-propr-1915-codex, taskId: integry-propr-1915-codex
# [live-details] Trying Redis fallback
# [live-details] stateKey: worker:state:integry-propr-1915-codex, hasData: false
# [live-details] No state data found in Redis
# [live-details] Fetching sessionId from SQLite for taskId: integry-propr-1915-codex
# [live-details] Found sessionId in SQLite: codex-db-session
# [live-details] Using sessionId: codex-db-session
# Subtest: Codex database lifecycle fallback has exact canonical event parity
ok 1 - Codex database lifecycle fallback has exact canonical event parity
  ---
  duration_ms: 3.482228
  type: 'test'
  ...
# Subtest: Codex command lifecycle pairs starts and completions and recovers an unmatched completion
ok 2 - Codex command lifecycle pairs starts and completions and recovers an unmatched completion
  ---
  duration_ms: 0.241584
  type: 'test'
  ...
# Subtest: Codex database fallback retains row content for minimal error metadata
ok 3 - Codex database fallback retains row content for minimal error metadata
  ---
  duration_ms: 0.138138
  type: 'test'
  ...
# Subtest: Codex database fallback retains row content and error flag for minimal tool-result metadata
ok 4 - Codex database fallback retains row content and error flag for minimal tool-result metadata
  ---
  duration_ms: 0.109975
  type: 'test'
  ...
# Subtest: Codex database fallback ignores envelopes while retaining text, errors, and todos
ok 5 - Codex database fallback ignores envelopes while retaining text, errors, and todos
  ---
  duration_ms: 0.31946
  type: 'test'
  ...
# [live-details] Checking Claude conversation path: not found
# [live-details] Claude conversation file not found, trying active Redis output
# [live-details] Claude conversation file not found, trying stored execution output fallback
# [live-details] No stored execution logs found in Redis for session fallback
# [live-details] Stored execution output fallback unavailable, trying database fallback
# [live-details] jobId: integry-propr-1915-codex, taskId: integry-propr-1915-codex
# [live-details] Trying Redis fallback
# [live-details] stateKey: worker:state:integry-propr-1915-codex, hasData: false
# [live-details] No state data found in Redis
# [live-details] Fetching sessionId from SQLite for taskId: integry-propr-1915-codex
# [live-details] Found sessionId in SQLite: codex-db-session
# [live-details] Using sessionId: codex-db-session
# [live-details] Checking Claude conversation path: not found
# [live-details] Claude conversation file not found, trying active Redis output
# [live-details] Claude conversation file not found, trying stored execution output fallback
# [live-details] No stored execution logs found in Redis for session fallback
# [live-details] Stored execution output fallback unavailable, trying database fallback
# Subtest: live-details database fallback preserves token usage and stable event IDs
ok 6 - live-details database fallback preserves token usage and stable event IDs
  ---
  duration_ms: 14.773634
  type: 'test'
  ...
# [2026-08-29 23:38:04.113 +0000] �[32mINFO�[39m: �[36mSQLite database connection established successfully�[39m
#     filename: "/tmp/propr-test-suite-hz71GE/017-liveDetailsCodexDbFallback.test.ts/propr.test.sqlite"
#     environment: "test"
1..6
# tests 6
# suites 0
# pass 6
# fail 0
# cancelled 0
# skipped 0
# todo 0
# duration_ms 1583.945978

[18/309] packages/api/test/liveDetailsOpenCodeParser.test.ts
TAP version 13
# Subtest: detectStoredOutputFormat keeps Codex message streams with session_id as codex
ok 1 - detectStoredOutputFormat keeps Codex message streams with session_id as codex
  ---
  duration_ms: 1198.875426
  type: 'test'
  ...
# Subtest: detectStoredOutputFormat does not classify bare conversation_id JSON as Claude
ok 2 - detectStoredOutputFormat does not classify bare conversation_id JSON as Claude
  ---
  duration_ms: 3.394877
  type: 'test'
  ...
# Subtest: detectStoredOutputFormat keeps Claude-shaped conversation_id JSON as Claude
ok 3 - detectStoredOutputFormat keeps Claude-shaped conversation_id JSON as Claude
  ---
  duration_ms: 1.174777
  type: 'test'
  ...
# Subtest: parseOpenCodeOutputToConversationResult separates structured assistant text parts
ok 4 - parseOpenCodeOutputToConversationResult separates structured assistant text parts
  ---
  duration_ms: 16.003829
  type: 'test'
  ...
# Subtest: detectStoredOutputFormat recognizes whole-document pretty OpenCode JSON
ok 5 - detectStoredOutputFormat recognizes whole-document pretty OpenCode JSON
  ---
  duration_ms: 1.093215
  type: 'test'
  ...
# Subtest: detectStoredOutputFormat recognizes OpenCode assistant parts without session identifiers
ok 6 - detectStoredOutputFormat recognizes OpenCode assistant parts without session identifiers
  ---
  duration_ms: 0.815607
  type: 'test'
  ...
# Subtest: detectStoredOutputFormat recognizes whole-document pretty Claude JSON arrays
ok 7 - detectStoredOutputFormat recognizes whole-document pretty Claude JSON arrays
  ---
  duration_ms: 0.701245
  type: 'test'
  ...
# [2026-08-29 23:38:05.788 +0000] �[32mINFO�[39m: �[36mSQLite database connection established successfully�[39m
#     filename: "/tmp/propr-test-suite-hz71GE/018-liveDetailsOpenCodeParser.test.ts/propr.test.sqlite"
#     environment: "test"
# [2026-08-29 23:38:05.812 +0000] �[32mINFO�[39m: �[36mSQLite database connection closed�[39m
1..7
# tests 7
# suites 0
# pass 7
# fail 0
# cancelled 0
# skipped 0
# todo 0
# duration_ms 1715.098737

[19/309] packages/api/test/mergedPrTaskCancellation.test.ts
TAP version 13
# [webhook] Merged PR acme/widgets\#42: cancelled 3/3 active task(s)
# [webhook] Failed to cancel task for merged PR {
#   taskId: 'task-a',
#   repository: 'acme/widgets',
#   prNumber: 42,
#   error: Error: container stop blew up
#       at Object.stopTask (/home/runner/work/propr/propr/packages/api/test/mergedPrTaskCancellation.test.ts:89:34)
#       at stopNext (/home/runner/work/propr/propr/packages/api/webhookHandler.ts:210:35)
#       at <anonymous> (/home/runner/work/propr/propr/packages/api/webhookHandler.ts:236:104)
#       at Function.from (<anonymous>)
#       at cancelActiveTasksForMergedPR (/home/runner/work/propr/propr/packages/api/webhookHandler.ts:236:27)
#       at async TestContext.<anonymous> (/home/runner/work/propr/propr/packages/api/test/mergedPrTaskCancellation.test.ts:94:19)
#       at async Test.run (node:internal/test_runner/test:1054:7)
#       at async Test.processPendingSubtests (node:internal/test_runner/test:744:7)
# }
# [webhook] Task task-a for merged PR acme/widgets\#42 was stopped but the cancellation was not durably recorded; the abort signal remains for the worker to observe
# [webhook] Merged PR acme/widgets\#42: cancelled 2/3 active task(s), 1 failed
# [webhook] Task task-a for merged PR acme/widgets\#42 was no longer active
# [webhook] Merged PR acme/widgets\#42: cancelled 2/3 active task(s), 1 no longer active
# [webhook] Merged PR acme/widgets\#42: cancelled 2/3 active task(s), 1 stopped without a durable record
# [webhook] Event received: pull_request, action: closed, repo: acme/widgets, delivery: delivery-3rscmn5bk8m
# [webhook] Merged PR acme/widgets\#42: cancelled 3/3 active task(s)
# [webhook] Event received: pull_request, action: closed, repo: acme/widgets, delivery: delivery-t22uwsdgg2
# [webhook] Event received: pull_request, action: closed, repo: acme/widgets, delivery: delivery-cxwoqjueuan
# [webhook] Merge-triggered task cancellation failed {
#   repository: 'acme/widgets',
#   prNumber: 42,
#   error: Error: database unavailable
#       at Object.getActiveTasksForPR (/home/runner/work/propr/propr/packages/api/test/mergedPrTaskCancellation.test.ts:202:46)
#       at cancelActiveTasksForMergedPR (/home/runner/work/propr/propr/packages/api/webhookHandler.ts:187:50)
#       at handleWebhookRequest (/home/runner/work/propr/propr/packages/api/webhookHandler.ts:333:28)
#       at async TestContext.<anonymous> (/home/runner/work/propr/propr/packages/api/test/mergedPrTaskCancellation.test.ts:207:3)
#       at async Test.run (node:internal/test_runner/test:1054:7)
#       at async Test.processPendingSubtests (node:internal/test_runner/test:744:7)
# }
# Subtest: isMergedPullRequestClose only matches merged PR close events
ok 1 - isMergedPullRequestClose only matches merged PR close events
  ---
  duration_ms: 0.955938
  type: 'test'
  ...
# Subtest: cancelActiveTasksForMergedPR stops every active task and queued job with the merge reason
ok 2 - cancelActiveTasksForMergedPR stops every active task and queued job with the merge reason
  ---
  duration_ms: 2.100811
  type: 'test'
  ...
# Subtest: cancelActiveTasksForMergedPR does nothing when there is no active work
ok 3 - cancelActiveTasksForMergedPR does nothing when there is no active work
  ---
  duration_ms: 0.673434
  type: 'test'
  ...
# Subtest: cancelActiveTasksForMergedPR continues cancelling after a task fails to stop
ok 4 - cancelActiveTasksForMergedPR continues cancelling after a task fails to stop
  ---
  duration_ms: 2.549065
  type: 'test'
  ...
# Subtest: cancelActiveTasksForMergedPR counts no-longer-active tasks as skipped
ok 5 - cancelActiveTasksForMergedPR counts no-longer-active tasks as skipped
  ---
  duration_ms: 0.461885
  type: 'test'
  ...
# Subtest: cancelActiveTasksForMergedPR surfaces stops without a durable cancellation record separately
ok 6 - cancelActiveTasksForMergedPR surfaces stops without a durable cancellation record separately
  ---
  duration_ms: 0.36542
  type: 'test'
  ...
# Subtest: handleWebhookRequest cancels active tasks when a PR is merged
ok 7 - handleWebhookRequest cancels active tasks when a PR is merged
  ---
  duration_ms: 1.624214
  type: 'test'
  ...
# Subtest: handleWebhookRequest does not cancel anything for an unmerged PR close
ok 8 - handleWebhookRequest does not cancel anything for an unmerged PR close
  ---
  duration_ms: 0.333391
  type: 'test'
  ...
# Subtest: handleWebhookRequest still succeeds when the PR task lookup fails entirely
ok 9 - handleWebhookRequest still succeeds when the PR task lookup fails entirely
  ---
  duration_ms: 0.90358
  type: 'test'
  ...
# [2026-08-29 23:38:07.664 +0000] �[32mINFO�[39m: �[36mSQLite database connection established successfully�[39m
#     filename: "/tmp/propr-test-suite-hz71GE/019-mergedPrTaskCancellation.test.ts/propr.test.sqlite"
#     environment: "test"
1..9
# tests 9
# suites 0
# pass 9
# fail 0
# cancelled 0
# skipped 0
# todo 0
# duration_ms 1750.310626

[20/309] packages/api/test/notificationManagementRoutes.test.ts
TAP version 13
# Subtest: notification subscription management routes
    # Subtest: lists safe metadata and revokes an owned opaque subscription ID
    ok 1 - lists safe metadata and revokes an owned opaque subscription ID
      ---
      duration_ms: 2.379901
      type: 'test'
      ...
    # Subtest: sanitizes preference snapshots at both response boundaries
    ok 2 - sanitizes preference snapshots at both response boundaries
      ---
      duration_ms: 0.806704
      type: 'test'
      ...
    # Subtest: warns distinctly and without secrets for invalid VAPID configuration
    ok 3 - warns distinctly and without secrets for invalid VAPID configuration
      ---
      duration_ms: 1.265054
      type: 'test'
      ...
    1..3
ok 1 - notification subscription management routes
  ---
  duration_ms: 5.281306
  type: 'suite'
  ...
# [2026-08-29 23:38:09.494 +0000] �[32mINFO�[39m: �[36mSQLite database connection established successfully�[39m
#     filename: "/tmp/propr-test-suite-hz71GE/020-notificationManagementRoutes.test.ts/propr.test.sqlite"
#     environment: "test"
# [2026-08-29 23:38:09.495 +0000] �[32mINFO�[39m: �[36mSQLite database connection closed�[39m
1..1
# tests 3
# suites 1
# pass 3
# fail 0
# cancelled 0
# skipped 0
# todo 0
# duration_ms 1730.731157

[21/309] packages/api/test/notificationProjectionService.test.ts
TAP version 13
# Subtest: notification lifecycle projection
    # Subtest: creates exactly one plan-ready event for the draft owner
    ok 1 - creates exactly one plan-ready event for the draft owner
      ---
      duration_ms: 125.403719
      type: 'test'
      ...
    # Subtest: separates implementation, review, and sanitized PR-attention events
    ok 2 - separates implementation, review, and sanitized PR-attention events
      ---
      duration_ms: 119.981101
      type: 'test'
      ...
    # Subtest: ignores stale task transitions and emits one stalled event per unchanged activity
    ok 3 - ignores stale task transitions and emits one stalled event per unchanged activity
      ---
      duration_ms: 92.125559
      type: 'test'
      ...
    # Subtest: projects a task failure once without copying error details
    ok 4 - projects a task failure once without copying error details
      ---
      duration_ms: 100.414471
      type: 'test'
      ...
    # Subtest: does not advertise Open PR when a trusted GitHub URL cannot be constructed
    ok 5 - does not advertise Open PR when a trusted GitHub URL cannot be constructed
      ---
      duration_ms: 91.380549
      type: 'test'
      ...
    # Subtest: advertises follow-up only with the stored repository and issue identity the endpoint requires
    ok 6 - advertises follow-up only with the stored repository and issue identity the endpoint requires
      ---
      duration_ms: 107.473312
      type: 'test'
      ...
    # Subtest: advertises review follow-up only when the endpoint issue is the reviewed PR
    ok 7 - advertises review follow-up only when the endpoint issue is the reviewed PR
      ---
      duration_ms: 102.924087
      type: 'test'
      ...
    # Subtest: restricts indexing failures to administrators
    ok 8 - restricts indexing failures to administrators
      ---
      duration_ms: 105.41449
      type: 'test'
      ...
    # Subtest: deduplicates one unhealthy period and allows a later failure after recovery
    ok 9 - deduplicates one unhealthy period and allows a later failure after recovery
      ---
      duration_ms: 96.437444
      type: 'test'
      ...
    # Subtest: logs and isolates projection persistence failures
    ok 10 - logs and isolates projection persistence failures
      ---
      duration_ms: 81.614351
      type: 'test'
      ...
    1..10
ok 1 - notification lifecycle projection
  ---
  duration_ms: 1024.88552
  type: 'suite'
  ...
# [2026-08-29 23:38:11.376 +0000] �[32mINFO�[39m: �[36mSQLite database connection established successfully�[39m
#     filename: "/tmp/propr-test-suite-hz71GE/021-notificationProjectionService.test.ts/propr.test.sqlite"
#     environment: "test"
# [2026-08-29 23:38:12.399 +0000] �[32mINFO�[39m: �[36mSQLite database connection closed�[39m
1..1
# tests 10
# suites 1
# pass 10
# fail 0
# cancelled 0
# skipped 0
# todo 0
# duration_ms 3613.845213

[22/309] packages/api/test/notificationRoutes.test.ts
TAP version 13
# Subtest: notification routes
    # Subtest: uses only the authenticated user and clamps list limits
    ok 1 - uses only the authenticated user and clamps list limits
      ---
      duration_ms: 2.377777
      type: 'test'
      ...
    # Subtest: uses the authenticated user for receipt mutations
    ok 2 - uses the authenticated user for receipt mutations
      ---
      duration_ms: 1.042218
      type: 'test'
      ...
    # Subtest: returns 400 for malformed limits, cursors, and history flags
    ok 3 - returns 400 for malformed limits, cursors, and history flags
      ---
      duration_ms: 0.928467
      type: 'test'
      ...
    # Subtest: requires an authenticated identity even when handlers are called directly
    ok 4 - requires an authenticated identity even when handlers are called directly
      ---
      duration_ms: 0.226761
      type: 'test'
      ...
    # Subtest: returns Web Push capability without exposing private VAPID material
    ok 5 - returns Web Push capability without exposing private VAPID material
      ---
      duration_ms: 0.862848
      type: 'test'
      ...
    # Subtest: does not advertise invalid VAPID keys or a disabled dispatcher
    ok 6 - does not advertise invalid VAPID keys or a disabled dispatcher
      ---
      duration_ms: 0.934516
      type: 'test'
      ...
    # Subtest: derives the preference owner from authentication and passes sparse updates through
    ok 7 - derives the preference owner from authentication and passes sparse updates through
      ---
      duration_ms: 0.329856
      type: 'test'
      ...
    # Subtest: enrolls and revokes subscriptions only for the authenticated user
    ok 8 - enrolls and revokes subscriptions only for the authenticated user
      ---
      duration_ms: 0.847746
      type: 'test'
      ...
    # Subtest: maps invalid notification inputs to 400 responses
    ok 9 - maps invalid notification inputs to 400 responses
      ---
      duration_ms: 0.302535
      type: 'test'
      ...
    # Subtest: maps endpoint ownership conflicts to a 409 response
    ok 10 - maps endpoint ownership conflicts to a 409 response
      ---
      duration_ms: 0.42538
      type: 'test'
      ...
    # Subtest: maps subscription quotas and enrollment rate limits to actionable responses
    ok 11 - maps subscription quotas and enrollment rate limits to actionable responses
      ---
      duration_ms: 0.419651
      type: 'test'
      ...
    # Subtest: keeps demo Inbox reads available while the existing policy blocks mutations
    ok 12 - keeps demo Inbox reads available while the existing policy blocks mutations
      ---
      duration_ms: 34.833346
      type: 'test'
      ...
    1..12
ok 1 - notification routes
  ---
  duration_ms: 44.840341
  type: 'suite'
  ...
# [2026-08-29 23:38:15.335 +0000] �[32mINFO�[39m: �[36mSQLite database connection established successfully�[39m
#     filename: "/tmp/propr-test-suite-hz71GE/022-notificationRoutes.test.ts/propr.test.sqlite"
#     environment: "test"
# [2026-08-29 23:38:15.376 +0000] �[32mINFO�[39m: �[36mSQLite database connection closed�[39m
1..1
# tests 12
# suites 1
# pass 12
# fail 0
# cancelled 0
# skipped 0
# todo 0
# duration_ms 2284.789049

[23/309] packages/api/test/oauthState.test.ts
TAP version 13
# Subtest: GitHub OAuth authorization stores and sends a random state nonce
ok 1 - GitHub OAuth authorization stores and sends a random state nonce
  ---
  duration_ms: 2.030916
  type: 'test'
  ...
# Subtest: GitHub OAuth callback rejects a mismatched state before token exchange
ok 2 - GitHub OAuth callback rejects a mismatched state before token exchange
  ---
  duration_ms: 0.944802
  type: 'test'
  ...
# [2026-08-29 23:38:17.697 +0000] �[32mINFO�[39m: �[36mSQLite database connection established successfully�[39m
#     filename: "/tmp/propr-test-suite-hz71GE/023-oauthState.test.ts/propr.test.sqlite"
#     environment: "test"
# [2026-08-29 23:38:17.697 +0000] �[32mINFO�[39m: �[36mSQLite database connection closed�[39m
1..2
# tests 2
# suites 0
# pass 2
# fail 0
# cancelled 0
# skipped 0
# todo 0
# duration_ms 2258.091436

[24/309] packages/api/test/plannerAbortHandlers.test.ts
TAP version 13
# [abort] Plan generation aborted for draft generation-progress-race
# [abort] Plan refinement aborted for draft refinement-progress-race
# Subtest: planner abort handlers
    # Subtest: preserves a generation that completes before the conditional abort transition
    ok 1 - preserves a generation that completes before the conditional abort transition
      ---
      duration_ms: 5.326483
      type: 'test'
      ...
    # Subtest: retries the abort transition when the same generation run advances its trace
    ok 2 - retries the abort transition when the same generation run advances its trace
      ---
      duration_ms: 2.907794
      type: 'test'
      ...
    # Subtest: does not cancel a same-timestamp refinement restart after reading the old run snapshot
    ok 3 - does not cancel a same-timestamp refinement restart after reading the old run snapshot
      ---
      duration_ms: 2.170426
      type: 'test'
      ...
    # Subtest: retries the abort transition when the same refinement run metadata advances
    ok 4 - retries the abort transition when the same refinement run metadata advances
      ---
      duration_ms: 1.809392
      type: 'test'
      ...
    # Subtest: surfaces failed reconciliation for a legacy draft-wide abort signal
    ok 5 - surfaces failed reconciliation for a legacy draft-wide abort signal
      -

...(truncated)

View Workflow

@propr-dev

propr-dev Bot commented Aug 29, 2026

Copy link
Copy Markdown
Contributor Author

Applied the requested follow-up changes in commit e8e4761

Summary of Changes

Fixed the full-suite failure in notificationPublicEntrypoint.test.ts.

The isolated package fixture now provides the declared zod dependency, allowing syntheticAgents.d.ts and runtime exports to resolve correctly.

Verification:

  • Shared package typecheck passed.
  • Targeted test passed through the repository test runner.
  • git diff --check passed.
  • No commit created.
  • Full local run reached Redis-dependent tests but could not continue because Redis/Docker is unavailable locally; CI reported no other failing files.

🤖 Implementation Details

  • Model: GPT-5.6 Sol (high)
  • Requested By: @github-actions[bot]
  • Time: 10m 53s
  • Tokens: 2,371,105 (2,364,093 in / 7,012 out)
  • Cost: $1.76

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: 5465566683✓

…nt.test.ts](/tmp/git-processor/worktrees/integry/propr/pr-1993-followup-2026-08-29T23-45-15/test/notificationPublicEntrypoint.test.ts:26).

Fixed the full-suite failure in [notificationPublicEntrypoint.test.ts](/tmp/git-processor/worktrees/integry/propr/pr-1993-followup-2026-08-29T23-45-15/test/notificationPublicEntrypoint.test.ts:26).

The isolated package fixture now provides the declared `zod` dependency, allowing `syntheticAgents.d.ts` and runtime exports to resolve correctly.

Verification:

- Shared package typecheck passed.
- Targeted test passed through the repository test runner.
- `git diff --check` passed.
- No commit created.
- Full local run reached Redis-dependent tests but could not continue because Redis/Docker is unavailable locally; CI reported no other failing files.

PR: #1993
Comment by: @github-actions[bot] (ID: 5465566683)
Model: gpt-5.6-sol
@github-actions

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/309] packages/api/test/agentLoginRoutes.test.ts
TAP version 13
# Subtest: agent login session manager
    # Subtest: builds an allowlisted Docker login container without forwarding host secrets
    ok 1 - builds an allowlisted Docker login container without forwarding host secrets
      ---
      duration_ms: 2.937061
      type: 'test'
      ...
    # Subtest: maps a ProPR-managed account to the managed host root and marks its container ownership as safe to normalize
    ok 2 - maps a ProPR-managed account to the managed host root and marks its container ownership as safe to normalize
      ---
      duration_ms: 1.314759
      type: 'test'
      ...
    # Subtest: creates a ProPR-managed credential directory before starting Docker
    ok 3 - creates a ProPR-managed credential directory before starting Docker
      ---
      duration_ms: 2.321103
      type: 'test'
      ...
    # Subtest: fails clearly instead of resolving a default home path inside a containerized API
    ok 4 - fails clearly instead of resolving a default home path inside a containerized API
      ---
      duration_ms: 0.648286
      type: 'test'
      ...
    # Subtest: rejects unsafe credential roots and option-like image names
    ok 5 - rejects unsafe credential roots and option-like image names
      ---
      duration_ms: 0.437018
      type: 'test'
      ...
    # Subtest: streams sanitized output, accepts input, and records successful completion
    ok 6 - streams sanitized output, accepts input, and records successful completion
      ---
      duration_ms: 1.684525
      type: 'test'
      ...
    # Subtest: prevents concurrent logins that write the same credential directory
    ok 7 - prevents concurrent logins that write the same credential directory
      ---
      duration_ms: 0.658301
      type: 'test'
      ...
    # Subtest: pulls a missing agent image before creating the login container
    ok 8 - pulls a missing agent image before creating the login container
      ---
      duration_ms: 0.662167
      type: 'test'
      ...
    # Subtest: renews the session deadline when the user sends input
    ok 9 - renews the session deadline when the user sends input
      ---
      duration_ms: 0.666182
      type: 'test'
      ...
    # Subtest: sweeps only orphaned login containers from the current stack scope
    ok 10 - sweeps only orphaned login containers from the current stack scope
      ---
      duration_ms: 0.536417
      type: 'test'
      ...
    1..10
ok 1 - agent login session manager
  ---
  duration_ms: 13.153108
  type: 'suite'
  ...
# Subtest: agent login routes
    # Subtest: starts and returns only the requesting user login session
    ok 1 - starts and returns only the requesting user login session
      ---
      duration_ms: 0.799644
      type: 'test'
      ...
    # Subtest: accepts the same agent alias on start and follow-up calls, including while disabled
    ok 2 - accepts the same agent alias on start and follow-up calls, including while disabled
      ---
      duration_ms: 0.580493
      type: 'test'
      ...
    # Subtest: rejects interactive login for an unsupported agent type
    ok 3 - rejects interactive login for an unsupported agent type
      ---
      duration_ms: 0.548826
      type: 'test'
      ...
    1..3
ok 2 - agent login routes
  ---
  duration_ms: 2.069254
  type: 'suite'
  ...
# [2026-08-29 23:57:46.038 +0000] �[32mINFO�[39m: �[36mSQLite database connection established successfully�[39m
#     filename: "/tmp/propr-test-suite-65MsIP/001-agentLoginRoutes.test.ts/propr.test.sqlite"
#     environment: "test"
# [2026-08-29 23:57:46.049 +0000] �[32mINFO�[39m: �[36mSQLite database connection closed�[39m
# [2026-08-29 23:57:46.049 +0000] �[32mINFO�[39m: �[36mShutting down queue...�[39m
# [2026-08-29 23:57:46.049 +0000] �[32mINFO�[39m: �[36mQueue was never initialized, nothing to shutdown�[39m
1..2
# tests 13
# suites 2
# pass 13
# fail 0
# cancelled 0
# skipped 0
# todo 0
# duration_ms 2330.976726

[2/309] packages/api/test/agentRuntimeRoutes.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: 4.20562
      type: 'test'
      ...
    # Subtest: persists a failed state when queue submission fails
    ok 2 - persists a failed state when queue submission fails
      ---
      duration_ms: 0.895358
      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: 0.753124
      type: 'test'
      ...
    # Subtest: times out slow runtime package validation before queueing
    ok 4 - times out slow runtime package validation before queueing
      ---
      duration_ms: 8.822949
      type: 'test'
      ...
    # Subtest: returns package suggestions from configured runtimes
    ok 5 - returns package suggestions from configured runtimes
      ---
      duration_ms: 0.767806
      type: 'test'
      ...
    # Subtest: enforces PROPR_ADMIN_USERS when configured
    ok 6 - enforces PROPR_ADMIN_USERS when configured
      ---
      duration_ms: 0.31926
      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.284799
      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.373722
      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.460172
      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: 2.51172
      type: 'test'
      ...
    # Subtest: resolves the runtime build queue lazily when queueing
    ok 11 - resolves the runtime build queue lazily when queueing
      ---
      duration_ms: 0.870791
      type: 'test'
      ...
    # Subtest: reports apply load failures through the route response
    ok 12 - reports apply load failures through the route response
      ---
      duration_ms: 0.485842
      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.586142
      type: 'test'
      ...
    # Subtest: rejects runtime verification without runtime-management permission
    ok 14 - rejects runtime verification without runtime-management permission
      ---
      duration_ms: 0.2718
      type: 'test'
      ...
    1..14
ok 1 - agent runtime package routes
  ---
  duration_ms: 23.124373
  type: 'suite'
  ...
# [2026-08-29 23:57:48.160 +0000] �[32mINFO�[39m: �[36mSQLite database connection established successfully�[39m
#     filename: "/tmp/propr-test-suite-65MsIP/002-agentRuntimeRoutes.test.ts/propr.test.sqlite"
#     environment: "test"
# [2026-08-29 23:57:48.181 +0000] �[32mINFO�[39m: �[36mSQLite database connection closed�[39m
1..1
# tests 14
# suites 1
# pass 14
# fail 0
# cancelled 0
# skipped 0
# todo 0
# duration_ms 2113.880481

[3/309] packages/api/test/attachmentUploadCleanup.test.ts
TAP version 13
# Subtest: upload handler removes Multer files rejected before attachment processing
ok 1 - upload handler removes Multer files rejected before attachment processing
  ---
  duration_ms: 10.290538
  type: 'test'
  ...
# Subtest: attachment processing removes temporary and final files when persistence fails
ok 2 - attachment processing removes temporary and final files when persistence fails
  ---
  duration_ms: 5.096355
  type: 'test'
  ...
# Subtest: temporary cleanup refuses paths outside its configured root
ok 3 - temporary cleanup refuses paths outside its configured root
  ---
  duration_ms: 0.673584
  type: 'test'
  ...
# Subtest: temporary cleanup rejects dot-segment paths that resolve above its root
ok 4 - temporary cleanup rejects dot-segment paths that resolve above its root
  ---
  duration_ms: 0.537419
  type: 'test'
  ...
# Subtest: attachment processing rejects path-like draft IDs and still cleans its temp file
ok 5 - attachment processing rejects path-like draft IDs and still cleans its temp file
  ---
  duration_ms: 1.788943
  type: 'test'
  ...
# [2026-08-29 23:57:50.407 +0000] �[32mINFO�[39m: �[36mSQLite database connection established successfully�[39m
#     filename: "/tmp/propr-test-suite-65MsIP/003-attachmentUploadCleanup.test.ts/propr.test.sqlite"
#     environment: "test"
# [2026-08-29 23:57:50.425 +0000] �[32mINFO�[39m: �[36mSQLite database connection closed�[39m
1..5
# tests 5
# suites 0
# pass 5
# fail 0
# cancelled 0
# skipped 0
# todo 0
# duration_ms 2111.155988

[4/309] packages/api/test/authGithubTokens.test.ts
TAP version 13
# Refreshing GitHub token for user octocat (force=true)
# Successfully refreshed GitHub token for user octocat
# Refreshing GitHub token for user octocat (force=true)
# GitHub token refresh failed with status 503
# Refreshing GitHub token for user octocat (force=true)
# Subtest: isGitHubTokenExpired handles missing, future, exact, and past expiry values
ok 1 - isGitHubTokenExpired handles missing, future, exact, and past expiry values
  ---
  duration_ms: 2.33841
  type: 'test'
  ...
# Subtest: ensureAuthenticated refreshes an expired GitHub token before continuing
ok 2 - ensureAuthenticated refreshes an expired GitHub token before continuing
  ---
  duration_ms: 4.889299
  type: 'test'
  ...
# Subtest: ensureAuthenticated reports a temporary error when refresh fails recoverably
ok 3 - ensureAuthenticated reports a temporary error when refresh fails recoverably
  ---
  duration_ms: 1.207077
  type: 'test'
  ...
# Successfully refreshed GitHub token for user octocat
# Saved refreshed GitHub token for concurrent request by user octocat
# Refreshing GitHub token for user octocat (force=true)
# GitHub token refresh response missing access_token
# Refreshing GitHub token for user octocat (force=true)
# GitHub token refresh error: bad_refresh_token - The refresh token is invalid.
# Marked GitHub OAuth session for user octocat as requiring re-authentication (bad_refresh_token)
# GitHub token expired or revoked, attempting token refresh
# Refreshing GitHub token for user octocat (force=true)
# Successfully refreshed GitHub token for user octocat
# Token refresh successful, client should retry
# Subtest: ensureAuthenticated coalesces concurrent expired-token refreshes for one session
ok 4 - ensureAuthenticated coalesces concurrent expired-token refreshes for one session
  ---
  duration_ms: 11.976305
  type: 'test'
  ...
# Subtest: ensureAuthenticated rejects an expired GitHub token with no refresh token
ok 5 - ensureAuthenticated rejects an expired GitHub token with no refresh token
  ---
  duration_ms: 0.700935
  type: 'test'
  ...
# Subtest: ensureAuthenticated reports a temporary error when refresh omits an access token
ok 6 - ensureAuthenticated reports a temporary error when refresh omits an access token
  ---
  duration_ms: 0.795767
  type: 'test'
  ...
# Subtest: ensureAuthenticated clears the session after an unrecoverable refresh error
ok 7 - ensureAuthenticated clears the session after an unrecoverable refresh error
  ---
  duration_ms: 1.099655
  type: 'test'
  ...
# Subtest: GitHub route auth error emits TOKEN_REFRESHED after a successful refresh
ok 8 - GitHub route auth error emits TOKEN_REFRESHED after a successful refresh
  ---
  duration_ms: 0.852533
  type: 'test'
  ...
# [2026-08-29 23:57:53.133 +0000] �[32mINFO�[39m: �[36mSQLite database connection established successfully�[39m
#     filename: "/tmp/propr-test-suite-65MsIP/004-authGithubTokens.test.ts/propr.test.sqlite"
#     environment: "test"
# [2026-08-29 23:57:53.151 +0000] �[32mINFO�[39m: �[36mSQLite database connection closed�[39m
1..8
# tests 8
# suites 0
# pass 8
# fail 0
# cancelled 0
# skipped 0
# todo 0
# duration_ms 2625.660283

[5/309] packages/api/test/authRedirect.test.ts
TAP version 13
# Subtest: auth redirect allowlist treats FRONTEND_URL as exact host only
ok 1 - auth redirect allowlist treats FRONTEND_URL as exact host only
  ---
  duration_ms: 47.707246
  type: 'test'
  ...
# Subtest: proxy mode auth redirect fallback returns the hosted UI origin
ok 2 - proxy mode auth redirect fallback returns the hosted UI origin
  ---
  duration_ms: 8.519403
  type: 'test'
  ...
# Subtest: auth redirect allowlist permits subdomains only for explicit wildcard-style hosts
ok 3 - auth redirect allowlist permits subdomains only for explicit wildcard-style hosts
  ---
  duration_ms: 5.130672
  type: 'test'
  ...
# Subtest: auth redirect allowlist permits leading-dot COOKIE_DOMAIN subdomains
ok 4 - auth redirect allowlist permits leading-dot COOKIE_DOMAIN subdomains
  ---
  duration_ms: 4.073451
  type: 'test'
  ...
# Subtest: auth redirect allowlist permits exact additional hosts without permitting their subdomains
ok 5 - auth redirect allowlist permits exact additional hosts without permitting their subdomains
  ---
  duration_ms: 6.455226
  type: 'test'
  ...
# Subtest: auth redirect allowlist permits wildcard entries with protocol prefixes
ok 6 - auth redirect allowlist permits wildcard entries with protocol prefixes
  ---
  duration_ms: 6.216677
  type: 'test'
  ...
# Subtest: auth redirect allowlist rejects invalid URLs and non-http protocols
ok 7 - auth redirect allowlist rejects invalid URLs and non-http protocols
  ---
  duration_ms: 6.824732
  type: 'test'
  ...
# Subtest: auth redirect allowlist only permits cleartext HTTP for localhost
ok 8 - auth redirect allowlist only permits cleartext HTTP for localhost
  ---
  duration_ms: 6.103957
  type: 'test'
  ...
# Subtest: auth redirect allowlist permits configured local IP literals
ok 9 - auth redirect allowlist permits configured local IP literals
  ---
  duration_ms: 5.567951
  type: 'test'
  ...
# Subtest: auth redirect allowlist ignores malformed additional host entries
ok 10 - auth redirect allowlist ignores malformed additional host entries
  ---
  duration_ms: 5.36862
  type: 'test'
  ...
# [2026-08-29 23:57:55.864 +0000] �[32mINFO�[39m: �[36mSQLite database connection established successfully�[39m
#     filename: "/tmp/propr-test-suite-65MsIP/005-authRedirect.test.ts/propr.test.sqlite"
#     environment: "test"
# [2026-08-29 23:57:55.957 +0000] �[32mINFO�[39m: �[36mSQLite database connection closed�[39m
1..10
# tests 10
# suites 0
# pass 10
# fail 0
# cancelled 0
# skipped 0
# todo 0
# duration_ms 2570.473645

[6/309] packages/api/test/configAgentValidation.antigravity.test.ts
TAP version 13
# Subtest: agent config validation accepts antigravity and rejects gemini for new configs
ok 1 - agent config validation accepts antigravity and rejects gemini for new configs
  ---
  duration_ms: 1601.976103
  type: 'test'
  ...
# [2026-08-29 23:57:58.135 +0000] �[32mINFO�[39m: �[36mSQLite database connection established successfully�[39m
#     filename: "/tmp/propr-test-suite-65MsIP/006-configAgentValidation.antigravity.test.ts/propr.test.sqlite"
#     environment: "test"
1..1
# tests 1
# suites 0
# pass 1
# fail 0
# cancelled 0
# skipped 0
# todo 0
# duration_ms 2152.131014

[7/309] packages/api/test/configReloadSubscription.test.ts
TAP version 13
# Subtest: API config subscription serializes the startup reload with settings updates
ok 1 - API config subscription serializes the startup reload with settings updates
  ---
  duration_ms: 3.845047
  type: 'test'
  ...
# Subtest: API config subscription reloads repository updates
ok 2 - API config subscription reloads repository updates
  ---
  duration_ms: 0.337908
  type: 'test'
  ...
# Subtest: API config subscription ignores unrelated and malformed events
ok 3 - API config subscription ignores unrelated and malformed events
  ---
  duration_ms: 0.253222
  type: 'test'
  ...
# Subtest: a failed API settings reload does not block the next notification
ok 4 - a failed API settings reload does not block the next notification
  ---
  duration_ms: 0.375585
  type: 'test'
  ...
1..4
# tests 4
# suites 0
# pass 4
# fail 0
# cancelled 0
# skipped 0
# todo 0
# duration_ms 258.24747

[8/309] packages/api/test/configRoutesIndexingSummarization.test.ts
TAP version 13
# [2026-08-29 23:58:00.781 +0000] �[32mINFO�[39m: �[36mSQLite database connection established successfully�[39m
#     filename: "/tmp/propr-test-suite-65MsIP/008-configRoutesIndexingSummarization.test.ts/propr.test.sqlite"
#     environment: "test"
# [2026-08-29 23:58:00.817 +0000] �[32mINFO�[39m: �[36mRunning database migrations...�[39m
# Subtest: postSummarizationSettings trims model-specific aliases before saving
ok 1 - postSummarizationSettings trims model-specific aliases before saving
  ---
  duration_ms: 1860.325206
  type: 'test'
  ...
# Subtest: postSummarizationSettings rejects enabled summarization without a primary alias
ok 2 - postSummarizationSettings rejects enabled summarization without a primary alias
  ---
  duration_ms: 3.447809
  type: 'test'
  ...
# [2026-08-29 23:58:01.060 +0000] �[32mINFO�[39m: �[36mDatabase migrations completed successfully�[39m
# [2026-08-29 23:58:01.062 +0000] �[32mINFO�[39m: �[36mSuccessfully loaded agents configuration�[39m
#     agentCount: 2
# [2026-08-29 23:58:01.062 +0000] �[32mINFO�[39m: �[36mSuccessfully loaded summarization settings�[39m
#     summarization: {
#       "enabled": false,
#       "agent_alias": "",
#       "fallback_agent_alias": "",
#       "custom_prompt": ""
#     }
# [2026-08-29 23:58:01.064 +0000] �[32mINFO�[39m: �[36mSuccessfully saved summarization settings�[39m
#     summarization: {
#       "enabled": true,
#       "agent_alias": "codex:gpt-5.5",
#       "fallback_agent_alias": "fallback:gpt-5.4",
#       "custom_prompt": ""
#     }
# [2026-08-29 23:58:01.064 +0000] �[32mINFO�[39m: �[36mSuccessfully loaded summarization settings�[39m
#     summarization: {
#       "enabled": true,
#       "agent_alias": "codex:gpt-5.5",
#       "fallback_agent_alias": "fallback:gpt-5.4",
#       "custom_prompt": ""
#     }
# [2026-08-29 23:58:01.075 +0000] �[32mINFO�[39m: �[36mSQLite database connection closed�[39m
1..2
# tests 2
# suites 0
# pass 2
# fail 0
# cancelled 0
# skipped 0
# todo 0
# duration_ms 2281.215616

[9/309] packages/api/test/connectAuth.test.ts
TAP version 13
# Subtest: relay tunnel mode uses Connect without local OAuth credentials
ok 1 - relay tunnel mode uses Connect without local OAuth credentials
  ---
  duration_ms: 1.102078
  type: 'test'
  ...
# Subtest: relay tunnel mode preserves Connect for operator-supplied service endpoints
ok 2 - relay tunnel mode preserves Connect for operator-supplied service endpoints
  ---
  duration_ms: 0.203086
  type: 'test'
  ...
# Subtest: local relay mode uses Connect without a per-instance OAuth App
ok 3 - local relay mode uses Connect without a per-instance OAuth App
  ---
  duration_ms: 0.222355
  type: 'test'
  ...
# Subtest: off-tunnel relay inference rejects callbacks outside the exact loopback allowlist
ok 4 - off-tunnel relay inference rejects callbacks outside the exact loopback allowlist
  ---
  duration_ms: 0.314713
  type: 'test'
  ...
# Subtest: off-tunnel custom relay enrollment does not infer hosted Connect auth
ok 5 - off-tunnel custom relay enrollment does not infer hosted Connect auth
  ---
  duration_ms: 0.138248
  type: 'test'
  ...
# Subtest: literal example OAuth placeholders do not enable GitHub web auth
ok 6 - literal example OAuth placeholders do not enable GitHub web auth
  ---
  duration_ms: 0.12613
  type: 'test'
  ...
# Subtest: explicit custom GitHub web auth remains supported
ok 7 - explicit custom GitHub web auth remains supported
  ---
  duration_ms: 0.14663
  type: 'test'
  ...
# Subtest: explicit custom GitHub web auth wins over relay inference off-tunnel
ok 8 - explicit custom GitHub web auth wins over relay inference off-tunnel
  ---
  duration_ms: 0.161993
  type: 'test'
  ...
# Subtest: Connect authorization URL carries the exact callback and CSRF state
ok 9 - Connect authorization URL carries the exact callback and CSRF state
  ---
  duration_ms: 0.47178
  type: 'test'
  ...
# Subtest: redeems a Connect code server-to-server without exposing the relay token in the body
ok 10 - redeems a Connect code server-to-server without exposing the relay token in the body
  ---
  duration_ms: 23.522925
  type: 'test'
  ...
# Subtest: binds the Connect identity username to the validated token owner
ok 11 - binds the Connect identity username to the validated token owner
  ---
  duration_ms: 0.776849
  type: 'test'
  ...
1..11
# tests 11
# suites 0
# pass 11
# fail 0
# cancelled 0
# skipped 0
# todo 0
# duration_ms 426.462467

[10/309] packages/api/test/connectAuthCallback.test.ts
TAP version 13
# [2026-08-29 23:58:04.188 +0000] �[32mINFO�[39m: �[36mSQLite database connection established successfully�[39m
#     filename: "/tmp/propr-test-suite-65MsIP/010-connectAuthCallback.test.ts/propr.test.sqlite"
#     environment: "test"
# Subtest: Connect login preserves a validated redirect_to across Passport session regeneration
ok 1 - Connect login preserves a validated redirect_to across Passport session regeneration
  ---
  duration_ms: 3041.256052
  type: 'test'
  ...
# [2026-08-29 23:58:07.225 +0000] �[32mINFO�[39m: �[36mSQLite database connection closed�[39m
1..1
# tests 1
# suites 0
# pass 1
# fail 0
# cancelled 0
# skipped 0
# todo 0
# duration_ms 5555.50621

[11/309] packages/api/test/corsValidation.test.ts
TAP version 13
# Subtest: CORS allows the hosted UI origin under proxy mode
ok 1 - CORS allows the hosted UI origin under proxy mode
  ---
  duration_ms: 0.981797
  type: 'test'
  ...
# Subtest: CORS rejects unrelated origins under proxy mode
ok 2 - CORS rejects unrelated origins under proxy mode
  ---
  duration_ms: 0.2392
  type: 'test'
  ...
# Subtest: CORS allows requests with no origin
ok 3 - CORS allows requests with no origin
  ---
  duration_ms: 0.163025
  type: 'test'
  ...
# Subtest: CORS allows localhost for development
ok 4 - CORS allows localhost for development
  ---
  duration_ms: 0.134712
  type: 'test'
  ...
# Subtest: CORS rejects non-http(s) localhost schemes
ok 5 - CORS rejects non-http(s) localhost schemes
  ---
  duration_ms: 0.196045
  type: 'test'
  ...
# Subtest: CORS allows COOKIE_DOMAIN subdomains for preview environments
ok 6 - CORS allows COOKIE_DOMAIN subdomains for preview environments
  ---
  duration_ms: 0.157367
  type: 'test'
  ...
# Subtest: CORS preserves http COOKIE_DOMAIN preview compatibility
ok 7 - CORS preserves http COOKIE_DOMAIN preview compatibility
  ---
  duration_ms: 0.169645
  type: 'test'
  ...
# Subtest: CORS validator factory throws on an invalid FRONTEND_URL
ok 8 - CORS validator factory throws on an invalid FRONTEND_URL
  ---
  duration_ms: 0.242595
  type: 'test'
  ...
# Subtest: Express CORS boundary is sanitized in development mode
ok 9 - Express CORS boundary is sanitized in development mode
  ---
  duration_ms: 82.937707
  type: 'test'
  ...
# Subtest: Express CORS boundary is sanitized in production mode
ok 10 - Express CORS boundary is sanitized in production mode
  ---
  duration_ms: 26.805365
  type: 'test'
  ...
1..10
# tests 10
# suites 0
# pass 10
# fail 0
# cancelled 0
# skipped 0
# todo 0
# duration_ms 504.684058

[12/309] packages/api/test/demoMode.test.ts
TAP version 13
# Subtest: demoModeReadOnlyMiddleware rejects mutating requests in demo mode
ok 1 - demoModeReadOnlyMiddleware rejects mutating requests in demo mode
  ---
  duration_ms: 5.978589
  type: 'test'
  ...
# Subtest: demoModeReadOnlyMiddleware blocks auth metadata mutations
ok 2 - demoModeReadOnlyMiddleware blocks auth metadata mutations
  ---
  duration_ms: 1.839259
  type: 'test'
  ...
# Subtest: isDemoMode accepts common truthy environment values
ok 3 - isDemoMode accepts common truthy environment values
  ---
  duration_ms: 0.991983
  type: 'test'
  ...
# Subtest: configured demo mode keeps auth and middleware on the same startup value
ok 4 - configured demo mode keeps auth and middleware on the same startup value
  ---
  duration_ms: 1.294519
  type: 'test'
  ...
# Subtest: demo Redis facade covers read-only route Redis usage
ok 5 - demo Redis facade covers read-only route Redis usage
  ---
  duration_ms: 3.322261
  type: 'test'
  ...
# Subtest: demo Express GET routes work with the in-memory Redis facade
ok 6 - demo Express GET routes work with the in-memory Redis facade
  ---
  duration_ms: 58.559789
  type: 'test'
  ...
# Subtest: ensureAuthenticated attaches the synthetic demo user
ok 7 - ensureAuthenticated attaches the synthetic demo user
  ---
  duration_ms: 1.051793
  type: 'test'
  ...
# Subtest: ensureAuthenticated ignores bearer auth and attaches the synthetic demo user in demo mode
ok 8 - ensureAuthenticated ignores bearer auth and attaches the synthetic demo user in demo mode
  ---
  duration_ms: 0.942328
  type: 'test'
  ...
# Subtest: demo repository metadata resolves enabled configured repositories
ok 9 - demo repository metadata resolves enabled configured repositories
  ---
  duration_ms: 9.456843
  type: 'test'
  ...
# Subtest: repository config branch validation documents ProPR-supported branch names
ok 10 - repository config branch validation documents ProPR-supported branch names
  ---
  duration_ms: 2.235514
  type: 'test'
  ...
# [2026-08-29 23:58:10.590 +0000] �[32mINFO�[39m: �[36mSQLite database connection established successfully�[39m
#     filename: "/tmp/propr-test-suite-65MsIP/012-demoMode.test.ts/propr.test.sqlite"
#     environment: "test"
# Subtest: /api/github/repos returns configured and persisted repositories in demo mode
ok 11 - /api/github/repos returns configured and persisted repositories in demo mode
  ---
  duration_ms: 252.180385
  type: 'test'
  ...
# Subtest: demo repository metadata resolves persisted repositories without configured allowlists
ok 12 - demo repository metadata resolves persisted repositories without configured allowlists
  ---
  duration_ms: 9.465737
  type: 'test'
  ...
# Subtest: demo repository metadata ignores malformed database repository names
ok 13 - demo repository metadata ignores malformed database repository names
  ---
  duration_ms: 1.578316
  type: 'test'
  ...
# [2026-08-29 23:58:10.921 +0000] �[32mINFO�[39m: �[36mSuccessfully saved monitored repositories�[39m
#     repos: [
#       {
#         "id": "1",
#         "name": "integry/propr",
#         "enabled": true
#       },
#       {
#         "id": "2",
#         "name": "integry/private",
#         "enabled": true
#       },
#       {
#         "id": "3",
#         "name": "integry/disabled",
#         "enabled": false
#       }
#     ]
# [2026-08-29 23:58:10.922 +0000] �[32mINFO�[39m: �[36mSuccessfully loaded all monitored repositories�[39m
#     total_repos: 3
# [2026-08-29 23:58:10.932 +0000] �[32mINFO�[39m: �[36mSuccessfully loaded all monitored repositories�[39m
#     total_repos: 0
# Subtest: planner demo reads use the curated database without owner or repository allowlists
ok 14 - planner demo reads use the curated database without owner or repository allowlists
  ---
  duration_ms: 10.454214
  type: 'test'
  ...
# [2026-08-29 23:58:10.942 +0000] �[32mINFO�[39m: �[36mSuccessfully saved monitored repositories�[39m
#     repos: [
#       {
#         "id": "1",
#         "name": "integry/propr",
#         "enabled": true
#       },
#       {
#         "id": "2",
#         "name": "integry/private",
#         "enabled": true
#       }
#     ]
# Subtest: repo todo demo reads use the curated database without owner filters
ok 15 - repo todo demo reads use the curated database without owner filters
  ---
  duration_ms: 20.555639
  type: 'test'
  ...
# Subtest: auth demo-mode metadata endpoint reports startup environment value
ok 16 - auth demo-mode metadata endpoint reports startup environment value
  ---
  duration_ms: 13.561341
  type: 'test'
  ...
1..16
# tests 16
# suites 0
# pass 16
# fail 0
# cancelled 0
# skipped 0
# todo 0
# duration_ms 2783.012491

[13/309] packages/api/test/dockerCommandSafety.test.ts
TAP version 13
# Subtest: accepts Docker IDs and ProPR-generated container names
ok 1 - accepts Docker IDs and ProPR-generated container names
  ---
  duration_ms: 0.874476
  type: 'test'
  ...
# Subtest: rejects container references that could be parsed as options or shell syntax
ok 2 - rejects container references that could be parsed as options or shell syntax
  ---
  duration_ms: 0.150777
  type: 'test'
  ...
# Subtest: production subprocess call sites do not invoke a command shell
ok 3 - production subprocess call sites do not invoke a command shell
  ---
  duration_ms: 71.201426
  type: 'test'
  ...
# Subtest: no-shell guard rejects aliases and enabled shell options
ok 4 - no-shell guard rejects aliases and enabled shell options
  ---
  duration_ms: 3.783144
  type: 'test'
  ...
1..4
# tests 4
# suites 0
# pass 4
# fail 0
# cancelled 0
# skipped 0
# todo 0
# duration_ms 941.576172

[14/309] packages/api/test/instanceAuthorization.test.ts
TAP version 13
# 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: 12.688378
      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: 4.901307
      type: 'test'
      ...
    # Subtest: grants full admin permissions to PROPR_ADMIN_USERS
    ok 3 - grants full admin permissions to PROPR_ADMIN_USERS
      ---
      duration_ms: 2.713524
      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: 2.558441
      type: 'test'
      ...
    # Subtest: resolves demo users without installation permissions
    ok 5 - resolves demo users without installation permissions
      ---
      duration_ms: 2.065278
      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: 1.898477
      type: 'test'
      ...
    # Subtest: current-user responses expose capabilities without OAuth credentials
    ok 7 - current-user responses expose capabilities without OAuth credentials
      ---
      duration_ms: 1.655151
      type: 'test'
      ...
    1..7
ok 1 - instance authorization
  ---
  duration_ms: 29.807725
  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: 3.224694
      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: 5.776324
      type: 'test'
      ...
    # Subtest: adds explicit members and writes an audit entry
    ok 3 - adds explicit members and writes an audit entry
      ---
      duration_ms: 9.243333
      type: 'test'
      ...
    # Subtest: keeps environment authority separate from durable role assignments
    ok 4 - keeps environment authority separate from durable role assignments
      ---
      duration_ms: 2.794857
      type: 'test'
      ...
    # Subtest: prevents removing the last durable administrator
    ok 5 - prevents removing the last durable administrator
      ---
      duration_ms: 4.148695
      type: 'test'
      ...
    # Subtest: allows role changes once another administrator exists
    ok 6 - allows role changes once another administrator exists
      ---
      duration_ms: 5.306146
      type: 'test'
      ...
    1..6
ok 2 - instance member service
  ---
  duration_ms: 30.907319
  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: 2.041403
      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: 1.619417
      type: 'test'
      ...
    1..2
ok 3 - instance catalog
  ---
  duration_ms: 3.748993
  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: 2.608396
      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: 1.329241
      type: 'test'
      ...
    # Subtest: rejects GitHub usernames with trailing or consecutive hyphens
    ok 3 - rejects GitHub usernames with trailing or consecutive hyphens
      ---
      duration_ms: 1.416522
      type: 'test'
      ...
    1..3
ok 4 - instance admin routes
  ---
  duration_ms: 5.465887
  type: 'suite'
  ...
# [2026-08-29 23:58:14.050 +0000] �[32mINFO�[39m: �[36mSQLite database connection established successfully�[39m
#     filename: "/tmp/propr-test-suite-65MsIP/014-instanceAuthorization.test.ts/propr.test.sqlite"
#     environment: "test"
# [2026-08-29 23:58:14.114 +0000] �[32mINFO�[39m: �[36mSQLite database connection closed�[39m
1..4
# tests 18
# suites 4
# pass 18
# fail 0
# cancelled 0
# skipped 0
# todo 0
# duration_ms 2163.066749

[15/309] packages/api/test/listenAddress.test.ts
TAP version 13
# Subtest: direct API execution binds to loopback by default
ok 1 - direct API execution binds to loopback by default
  ---
  duration_ms: 0.934106
  type: 'test'
  ...
# Subtest: containerized API execution remains reachable through its published port
ok 2 - containerized API execution remains reachable through its published port
  ---
  duration_ms: 0.153521
  type: 'test'
  ...
# Subtest: an explicit API listen host overrides environment defaults
ok 3 - an explicit API listen host overrides environment defaults
  ---
  duration_ms: 0.15325
  type: 'test'
  ...
1..3
# tests 3
# suites 0
# pass 3
# fail 0
# cancelled 0
# skipped 0
# todo 0
# duration_ms 256.894366

[16/309] packages/api/test/liveDetailsAntigravity.test.ts
TAP version 13
# Failed to read GitHub App private key: ENOENT: no such file or directory, open '/tmp/missing-key.pem'
# Ensure GH_PRIVATE_KEY_PATH is set correctly in your .env file and points to a valid private key file.
# Subtest: stored output detection recognizes Antigravity stream JSON
ok 1 - stored output detection recognizes Antigravity stream JSON
  ---
  duration_ms: 2.142625
  type: 'test'
  ...
# Subtest: stored output detection keeps Codex message JSONL classified as Codex
ok 2 - stored output detection keeps Codex message JSONL classified as Codex
  ---
  duration_ms: 1.36196
  type: 'test'
  ...
# Subtest: stored output detection keeps Codex result JSONL classified as Codex
ok 3 - stored output detection keeps Codex result JSONL classified as Codex
  ---
  duration_ms: 0.827896
  type: 'test'
  ...
# Subtest: stored output detection keeps Codex tool result JSONL classified as Codex
ok 4 - stored output detection keeps Codex tool result JSONL classified as Codex
  ---
  duration_ms: 0.21912
  type: 'test'
  ...
# Subtest: stored output detection does not treat generic init JSONL as Antigravity
ok 5 - stored output detection does not treat generic init JSONL as Antigravity
  ---
  duration_ms: 0.289696
  type: 'test'
  ...
# Subtest: stored output detection recognizes Antigravity JSONL from result stats when init is missing
ok 6 - stored output detection recognizes Antigravity JSONL from result stats when init is missing
  ---
  duration_ms: 0.321894
  type: 'test'
  ...
# Subtest: stored output detection keeps generic result token stats classified as Codex
ok 7 - stored output detection keeps generic result token stats classified as Codex
  ---
  duration_ms: 0.240543
  type: 'test'
  ...
# Subtest: stored output detection recognizes truncated Antigravity result JSON
ok 8 - stored output detection recognizes truncated Antigravity result JSON
  ---
  duration_ms: 0.156405
  type: 'test'
  ...
# Subtest: stored output detection recognizes Antigravity message JSON with model metadata
ok 9 - stored output detection recognizes Antigravity message JSON with model metadata
  ---
  duration_ms: 0.407403
  type: 'test'
  ...
# Subtest: stored output parsing renders only Antigravity analysis events through live details
ok 10 - stored output parsing renders only Antigravity analysis events through live details
  ---
  duration_ms: 68.209063
  type: 'test'
  ...
# Subtest: Antigravity 1.1.12 stream text remains visible through live details
ok 11 - Antigravity 1.1.12 stream text remains visible through live details
  ---
  duration_ms: 1.963786
  type: 'test'
  ...
# Subtest: stored output detection and live-details rendering consume Antigravity stream arrays
ok 12 - stored output detection and live-details rendering consume Antigravity stream arrays
  ---
  duration_ms: 1.460748
  type: 'test'
  ...
# Subtest: Antigravity cache-only stream usage remains visible through live details
ok 13 - Antigravity cache-only stream usage remains visible through live details
  ---
  duration_ms: 0.935729
  type: 'test'
  ...
# Subtest: stored output parsing filters Antigravity transcript tool items
ok 14 - stored output parsing filters Antigravity transcript tool items
  ---
  duration_ms: 1.215108
  type: 'test'
  ...
# Subtest: Claude-format Antigravity conversation files render only planner analysis
ok 15 - Claude-format Antigravity conversation files render only planner analysis
  ---
  duration_ms: 1.367348
  type: 'test'
  ...
# Subtest: execution detail fallback filters Antigravity transcript tool rows
ok 16 - execution detail fallback filters Antigravity transcript tool rows
  ---
  duration_ms: 1.002198
  type: 'test'
  ...
# [2026-08-29 23:58:16.778 +0000] �[32mINFO�[39m: �[36mSQLite database connection established successfully�[39m
#     filename: "/tmp/propr-test-suite-65MsIP/016-liveDetailsAntigravity.test.ts/propr.test.sqlite"
#     environment: "test"
1..16
# tests 16
# suites 0
# pass 16
# fail 0
# cancelled 0
# skipped 0
# todo 0
# duration_ms 2140.252766

[17/309] packages/api/test/liveDetailsCodexDbFallback.test.ts
TAP version 13
# [live-details] jobId: integry-propr-1915-codex, taskId: integry-propr-1915-codex
# [live-details] Trying Redis fallback
# [live-details] stateKey: worker:state:integry-propr-1915-codex, hasData: false
# [live-details] No state data found in Redis
# [live-details] Fetching sessionId from SQLite for taskId: integry-propr-1915-codex
# [live-details] Found sessionId in SQLite: codex-db-session
# [live-details] Using sessionId: codex-db-session
# Subtest: Codex database lifecycle fallback has exact canonical event parity
ok 1 - Codex database lifecycle fallback has exact canonical event parity
  ---
  duration_ms: 4.173341
  type: 'test'
  ...
# Subtest: Codex command lifecycle pairs starts and completions and recovers an unmatched completion
ok 2 - Codex command lifecycle pairs starts and completions and recovers an unmatched completion
  ---
  duration_ms: 0.256666
  type: 'test'
  ...
# Subtest: Codex database fallback retains row content for minimal error metadata
ok 3 - Codex database fallback retains row content for minimal error metadata
  ---
  duration_ms: 0.178629
  type: 'test'
  ...
# Subtest: Codex database fallback retains row content and error flag for minimal tool-result metadata
ok 4 - Codex database fallback retains row content and error flag for minimal tool-result metadata
  ---
  duration_ms: 0.13897
  type: 'test'
  ...
# Subtest: Codex database fallback ignores envelopes while retaining text, errors, and todos
ok 5 - Codex database fallback ignores envelopes while retaining text, errors, and todos
  ---
  duration_ms: 0.390708
  type: 'test'
  ...
# [live-details] Checking Claude conversation path: not found
# [live-details] Claude conversation file not found, trying active Redis output
# [live-details] Claude conversation file not found, trying stored execution output fallback
# [live-details] No stored execution logs found in Redis for session fallback
# [live-details] Stored execution output fallback unavailable, trying database fallback
# [live-details] jobId: integry-propr-1915-codex, taskId: integry-propr-1915-codex
# [live-details] Trying Redis fallback
# [live-details] stateKey: worker:state:integry-propr-1915-codex, hasData: false
# [live-details] No state data found in Redis
# [live-details] Fetching sessionId from SQLite for taskId: integry-propr-1915-codex
# [live-details] Found sessionId in SQLite: codex-db-session
# [live-details] Using sessionId: codex-db-session
# [live-details] Checking Claude conversation path: not found
# [live-details] Claude conversation file not found, trying active Redis output
# [live-details] Claude conversation file not found, trying stored execution output fallback
# [live-details] No stored execution logs found in Redis for session fallback
# [live-details] Stored execution output fallback unavailable, trying database fallback
# Subtest: live-details database fallback preserves token usage and stable event IDs
ok 6 - live-details database fallback preserves token usage and stable event IDs
  ---
  duration_ms: 17.899189
  type: 'test'
  ...
# [2026-08-29 23:58:18.982 +0000] �[32mINFO�[39m: �[36mSQLite database connection established successfully�[39m
#     filename: "/tmp/propr-test-suite-65MsIP/017-liveDetailsCodexDbFallback.test.ts/propr.test.sqlite"
#     environment: "test"
1..6
# tests 6
# suites 0
# pass 6
# fail 0
# cancelled 0
# skipped 0
# todo 0
# duration_ms 1880.286662

[18/309] packages/api/test/liveDetailsOpenCodeParser.test.ts
TAP version 13
# Subtest: detectStoredOutputFormat keeps Codex message streams with session_id as codex
ok 1 - detectStoredOutputFormat keeps Codex message streams with session_id as codex
  ---
  duration_ms: 1560.011159
  type: 'test'
  ...
# Subtest: detectStoredOutputFormat does not classify bare conversation_id JSON as Claude
ok 2 - detectStoredOutputFormat does not classify bare conversation_id JSON as Claude
  ---
  duration_ms: 5.768202
  type: 'test'
  ...
# Subtest: detectStoredOutputFormat keeps Claude-shaped conversation_id JSON as Claude
ok 3 - detectStoredOutputFormat keeps Claude-shaped conversation_id JSON as Claude
  ---
  duration_ms: 1.526818
  type: 'test'
  ...
# Subtest: parseOpenCodeOutputToConversationResult separates structured assistant text parts
ok 4 - parseOpenCodeOutputToConversationResult separates structured assistant text parts
  ---
  duration_ms: 18.14192
  type: 'test'
  ...
# Subtest: detectStoredOutputFormat recognizes whole-document pretty OpenCode JSON
ok 5 - detectStoredOutputFormat recognizes whole-document pretty OpenCode JSON
  ---
  duration_ms: 8.946525
  type: 'test'
  ...
# Subtest: detectStoredOutputFormat recognizes OpenCode assistant parts without session identifiers
ok 6 - detectStoredOutputFormat recognizes OpenCode assistant parts without session identifiers
  ---
  duration_ms: 2.230768
  type: 'test'
  ...
# Subtest: detectStoredOutputFormat recognizes whole-document pretty Claude JSON arrays
ok 7 - detectStoredOutputFormat recognizes whole-document pretty Claude JSON arrays
  ---
  duration_ms: 0.683409
  type: 'test'
  ...
# [2026-08-29 23:58:21.028 +0000] �[32mINFO�[39m: �[36mSQLite database connection established successfully�[39m
#     filename: "/tmp/propr-test-suite-65MsIP/018-liveDetailsOpenCodeParser.test.ts/propr.test.sqlite"
#     environment: "test"
# [2026-08-29 23:58:21.066 +0000] �[32mINFO�[39m: �[36mSQLite database connection closed�[39m
1..7
# tests 7
# suites 0
# pass 7
# fail 0
# cancelled 0
# skipped 0
# todo 0
# duration_ms 1990.124491

[19/309] packages/api/test/mergedPrTaskCancellation.test.ts
TAP version 13
# [webhook] Merged PR acme/widgets\#42: cancelled 3/3 active task(s)
# [webhook] Failed to cancel task for merged PR {
#   taskId: 'task-a',
#   repository: 'acme/widgets',
#   prNumber: 42,
#   error: Error: container stop blew up
#       at Object.stopTask (/home/runner/work/propr/propr/packages/api/test/mergedPrTaskCancellation.test.ts:89:34)
#       at stopNext (/home/runner/work/propr/propr/packages/api/webhookHandler.ts:210:35)
#       at <anonymous> (/home/runner/work/propr/propr/packages/api/webhookHandler.ts:236:104)
#       at Function.from (<anonymous>)
#       at cancelActiveTasksForMergedPR (/home/runner/work/propr/propr/packages/api/webhookHandler.ts:236:27)
#       at async TestContext.<anonymous> (/home/runner/work/propr/propr/packages/api/test/mergedPrTaskCancellation.test.ts:94:19)
#       at async Test.run (node:internal/test_runner/test:1054:7)
#       at async Test.processPendingSubtests (node:internal/test_runner/test:744:7)
# }
# [webhook] Task task-a for merged PR acme/widgets\#42 was stopped but the cancellation was not durably recorded; the abort signal remains for the worker to observe
# [webhook] Merged PR acme/widgets\#42: cancelled 2/3 active task(s), 1 failed
# [webhook] Task task-a for merged PR acme/widgets\#42 was no longer active
# [webhook] Merged PR acme/widgets\#42: cancelled 2/3 active task(s), 1 no longer active
# [webhook] Merged PR acme/widgets\#42: cancelled 2/3 active task(s), 1 stopped without a durable record
# [webhook] Event received: pull_request, action: closed, repo: acme/widgets, delivery: delivery-40fs4sdp3ab
# [webhook] Merged PR acme/widgets\#42: cancelled 3/3 active task(s)
# [webhook] Event received: pull_request, action: closed, repo: acme/widgets, delivery: delivery-zvl4uhjl0td
# [webhook] Event received: pull_request, action: closed, repo: acme/widgets, delivery: delivery-wsg7wml8ll8
# [webhook] Merge-triggered task cancellation failed {
#   repository: 'acme/widgets',
#   prNumber: 42,
#   error: Error: database unavailable
#       at Object.getActiveTasksForPR (/home/runner/work/propr/propr/packages/api/test/mergedPrTaskCancellation.test.ts:202:46)
#       at cancelActiveTasksForMergedPR (/home/runner/work/propr/propr/packages/api/webhookHandler.ts:187:50)
#       at handleWebhookRequest (/home/runner/work/propr/propr/packages/api/webhookHandler.ts:333:28)
#       at async TestContext.<anonymous> (/home/runner/work/propr/propr/packages/api/test/mergedPrTaskCancellation.test.ts:207:3)
#       at async Test.run (node:internal/test_runner/test:1054:7)
#       at async Test.processPendingSubtests (node:internal/test_runner/test:744:7)
# }
# Subtest: isMergedPullRequestClose only matches merged PR close events
ok 1 - isMergedPullRequestClose only matches merged PR close events
  ---
  duration_ms: 1.216721
  type: 'test'
  ...
# Subtest: cancelActiveTasksForMergedPR stops every active task and queued job with the merge reason
ok 2 - cancelActiveTasksForMergedPR stops every active task and queued job with the merge reason
  ---
  duration_ms: 2.719363
  type: 'test'
  ...
# Subtest: cancelActiveTasksForMergedPR does nothing when there is no active work
ok 3 - cancelActiveTasksForMergedPR does nothing when there is no active work
  ---
  duration_ms: 0.866845
  type: 'test'
  ...
# Subtest: cancelActiveTasksForMergedPR continues cancelling after a task fails to stop
ok 4 - cancelActiveTasksForMergedPR continues cancelling after a task fails to stop
  ---
  duration_ms: 3.264403
  type: 'test'
  ...
# Subtest: cancelActiveTasksForMergedPR counts no-longer-active tasks as skipped
ok 5 - cancelActiveTasksForMergedPR counts no-longer-active tasks as skipped
  ---
  duration_ms: 0.5585
  type: 'test'
  ...
# Subtest: cancelActiveTasksForMergedPR surfaces stops without a durable cancellation record separately
ok 6 - cancelActiveTasksForMergedPR surfaces stops without a durable cancellation record separately
  ---
  duration_ms: 0.388595
  type: 'test'
  ...
# Subtest: handleWebhookRequest cancels active tasks when a PR is merged
ok 7 - handleWebhookRequest cancels active tasks when a PR is merged
  ---
  duration_ms: 1.703124
  type: 'test'
  ...
# Subtest: handleWebhookRequest does not cancel anything for an unmerged PR close
ok 8 - handleWebhookRequest does not cancel anything for an unmerged PR close
  ---
  duration_ms: 0.278629
  type: 'test'
  ...
# Subtest: handleWebhookRequest still succeeds when the PR task lookup fails entirely
ok 9 - handleWebhookRequest still succeeds when the PR task lookup fails entirely
  ---
  duration_ms: 1.020526
  type: 'test'
  ...
# [2026-08-29 23:58:23.143 +0000] �[32mINFO�[39m: �[36mSQLite database connection established successfully�[39m
#     filename: "/tmp/propr-test-suite-65MsIP/019-mergedPrTaskCancellation.test.ts/propr.test.sqlite"
#     environment: "test"
1..9
# tests 9
# suites 0
# pass 9
# fail 0
# cancelled 0
# skipped 0
# todo 0
# duration_ms 2104.261719

[20/309] packages/api/test/notificationManagementRoutes.test.ts
TAP version 13
# Subtest: notification subscription management routes
    # Subtest: lists safe metadata and revokes an owned opaque subscription ID
    ok 1 - lists safe metadata and revokes an owned opaque subscription ID
      ---
      duration_ms: 2.958122
      type: 'test'
      ...
    # Subtest: sanitizes preference snapshots at both response boundaries
    ok 2 - sanitizes preference snapshots at both response boundaries
      ---
      duration_ms: 0.916179
      type: 'test'
      ...
    # Subtest: warns distinctly and without secrets for invalid VAPID configuration
    ok 3 - warns distinctly and without secrets for invalid VAPID configuration
      ---
      duration_ms: 1.54805
      type: 'test'
      ...
    1..3
ok 1 - notification subscription management routes
  ---
  duration_ms: 6.422707
  type: 'suite'
  ...
# [2026-08-29 23:58:25.406 +0000] �[32mINFO�[39m: �[36mSQLite database connection established successfully�[39m
#     filename: "/tmp/propr-test-suite-65MsIP/020-notificationManagementRoutes.test.ts/propr.test.sqlite"
#     environment: "test"
# [2026-08-29 23:58:25.407 +0000] �[32mINFO�[39m: �[36mSQLite database connection closed�[39m
1..1
# tests 3
# suites 1
# pass 3
# fail 0
# cancelled 0
# skipped 0
# todo 0
# duration_ms 2120.586518

[21/309] packages/api/test/notificationProjectionService.test.ts
TAP version 13
# Subtest: notification lifecycle projection
    # Subtest: creates exactly one plan-ready event for the draft owner
    ok 1 - creates exactly one plan-ready event for the draft owner
      ---
      duration_ms: 155.561334
      type: 'test'
      ...
    # Subtest: separates implementation, review, and sanitized PR-attention events
    ok 2 - separates implementation, review, and sanitized PR-attention events
      ---
      duration_ms: 158.759077
      type: 'test'
      ...
    # Subtest: ignores stale task transitions and emits one stalled event per unchanged activity
    ok 3 - ignores stale task transitions and emits one stalled event per unchanged activity
      ---
      duration_ms: 136.541369
      type: 'test'
      ...
    # Subtest: projects a task failure once without copying error details
    ok 4 - projects a task failure once without copying error details
      ---
      duration_ms: 115.617568
      type: 'test'
      ...
    # Subtest: does not advertise Open PR when a trusted GitHub URL cannot be constructed
    ok 5 - does not advertise Open PR when a trusted GitHub URL cannot be constructed
      ---
      duration_ms: 122.683272
      type: 'test'
      ...
    # Subtest: advertises follow-up only with the stored repository and issue identity the endpoint requires
    ok 6 - advertises follow-up only with the stored repository and issue identity the endpoint requires
      ---
      duration_ms: 141.241683
      type: 'test'
      ...
    # Subtest: advertises review follow-up only when the endpoint issue is the reviewed PR
    ok 7 - advertises review follow-up only when the endpoint issue is the reviewed PR
      ---
      duration_ms: 147.029003
      type: 'test'
      ...
    # Subtest: restricts indexing failures to administrators
    ok 8 - restricts indexing failures to administrators
      ---
      duration_ms: 118.315389
      type: 'test'
      ...
    # Subtest: deduplicates one unhealthy period and allows a later failure after recovery
    ok 9 - deduplicates one unhealthy period and allows a later failure after recovery
      ---
      duration_ms: 132.89101
      type: 'test'
      ...
    # Subtest: logs and isolates projection persistence failures
    ok 10 - logs and isolates projection persistence failures
      ---
      duration_ms: 102.82214
      type: 'test'
      ...
    1..10
ok 1 - notification lifecycle projection
  ---
  duration_ms: 1333.373271
  type: 'suite'
  ...
# [2026-08-29 23:58:27.705 +0000] �[32mINFO�[39m: �[36mSQLite database connection established successfully�[39m
#     filename: "/tmp/propr-test-suite-65MsIP/021-notificationProjectionService.test.ts/propr.test.sqlite"
#     environment: "test"
# [2026-08-29 23:58:29.036 +0000] �[32mINFO�[39m: �[36mSQLite database connection closed�[39m
1..1
# tests 10
# suites 1
# pass 10
# fail 0
# cancelled 0
# skipped 0
# todo 0
# duration_ms 3754.902489

[22/309] packages/api/test/notificationRoutes.test.ts
TAP version 13
# Subtest: notification routes
    # Subtest: uses only the authenticated user and clamps list limits
    ok 1 - uses only the authenticated user and clamps list limits
      ---
      duration_ms: 3.100686
      type: 'test'
      ...
    # Subtest: uses the authenticated user for receipt mutations
    ok 2 - uses the authenticated user for receipt mutations
      ---
      duration_ms: 1.374649
      type: 'test'
      ...
    # Subtest: returns 400 for malformed limits, cursors, and history flags
    ok 3 - returns 400 for malformed limits, cursors, and history flags
      ---
      duration_ms: 1.237413
      type: 'test'
      ...
    # Subtest: requires an authenticated identity even when handlers are called directly
    ok 4 - requires an authenticated identity even when handlers are called directly
      ---
      duration_ms: 0.303326
      type: 'test'
      ...
    # Subtest: returns Web Push capability without exposing private VAPID material
    ok 5 - returns Web Push capability without exposing private VAPID material
      ---
      duration_ms: 1.12297
      type: 'test'
      ...
    # Subtest: does not advertise invalid VAPID keys or a disabled dispatcher
    ok 6 - does not advertise invalid VAPID keys or a disabled dispatcher
      ---
      duration_ms: 1.188188
      type: 'test'
      ...
    # Subtest: derives the preference owner from authentication and passes sparse updates through
    ok 7 - derives the preference owner from authentication and passes sparse updates through
      ---
      duration_ms: 0.427754
      type: 'test'
      ...
    # Subtest: enrolls and revokes subscriptions only for the authenticated user
    ok 8 - enrolls and revokes subscriptions only for the authenticated user
      ---
      duration_ms: 1.106896
      type: 'test'
      ...
    # Subtest: maps invalid notification inputs to 400 responses
    ok 9 - maps invalid notification inputs to 400 responses
      ---
      duration_ms: 0.384769
      type: 'test'
      ...
    # Subtest: maps endpoint ownership conflicts to a 409 response
    ok 10 - maps endpoint ownership conflicts to a 409 response
      ---
      duration_ms: 0.524499
      type: 'test'
      ...
    # Subtest: maps subscription quotas and enrollment rate limits to actionable responses
    ok 11 - maps subscription quotas and enrollment rate limits to actionable responses
      ---
      duration_ms: 0.543108
      type: 'test'
      ...
    # Subtest: keeps demo Inbox reads available while the existing policy blocks mutations
    ok 12 - keeps demo Inbox reads available while the existing policy blocks mutations
      ---
      duration_ms: 44.694797
      type: 'test'
      ...
    1..12
ok 1 - notification routes
  ---
  duration_ms: 57.783095
  type: 'suite'
  ...
# [2026-08-29 23:58:31.963 +0000] �[32mINFO�[39m: �[36mSQLite database connection established successfully�[39m
#     filename: "/tmp/propr-test-suite-65MsIP/022-notificationRoutes.test.ts/propr.test.sqlite"
#     environment: "test"
# [2026-08-29 23:58:32.016 +0000] �[32mINFO�[39m: �[36mSQLite database connection closed�[39m
1..1
# tests 12
# suites 1
# pass 12
# fail 0
# cancelled 0
# skipped 0
# todo 0
# duration_ms 2856.637485

[23/309] packages/api/test/oauthState.test.ts
TAP version 13
# Subtest: GitHub OAuth authorization stores and sends a random state nonce
ok 1 - GitHub OAuth authorization stores and sends a random state nonce
  ---
  duration_ms: 2.619633
  type: 'test'
  ...
# Subtest: GitHub OAuth callback rejects a mismatched state before token exchange
ok 2 - GitHub OAuth callback rejects a mismatched state before token exchange
  ---
  duration_ms: 1.239285
  type: 'test'
  ...
# [2026-08-29 23:58:34.834 +0000] �[32mINFO�[39m: �[36mSQLite database connection established successfully�[39m
#     filename: "/tmp/propr-test-suite-65MsIP/023-oauthState.test.ts/propr.test.sqlite"
#     environment: "test"
# [2026-08-29 23:58:34.835 +0000] �[32mINFO�[39m: �[36mSQLite database connection closed�[39m
1..2
# tests 2
# suites 0
# pass 2
# fail 0
# cancelled 0
# skipped 0
# todo 0
# duration_ms 2723.3553

[24/309] packages/api/test/plannerAbortHandlers.test.ts
TAP version 13
# [abort] Plan generation aborted for draft generation-progress-race
# [abort] Plan refinement aborted for draft refinement-progress-race
# Subtest: planner abort handlers
    # Subtest: preserves a generation that completes before the conditional abort transition
    ok 1 - preserves a generation that completes before the conditional abort transition
      ---
      duration_ms: 6.816069
      type: 'test'
      ...
    # Subtest: retries the abort transition when the same generation run advances its trace
    ok 2 - retries the abort transition when the same generation run advances its trace
      ---
      duration_ms: 3.435261
      type: 'test'
      ...
    # Subtest: does not cancel a same-timestamp refinement restart after reading the old run snapshot
    ok 3 - does not cancel a same-timestamp refinement restart after reading the old run snapshot
      ---
      duration_ms: 2.694145
      type: 'test'
      ...
    # Subtest: retries the abort transition when the same refinement run metadata advances
    ok 4 - retries the abort transition when the same refinement run metadata advances
      ---
      duration_ms: 2.22575
      type: 'test'
      ...
    # Subtest: surfaces failed reconciliation for a legacy draft-wide abort signal
    ok 5 - surfaces failed reconciliation for a legacy draft-wide abort signa

...(truncated)

View Workflow

@propr-dev

propr-dev Bot commented Aug 30, 2026

Copy link
Copy Markdown
Contributor Author

Applied the requested follow-up changes in commit 7348f75

Summary of Changes

Fixed the full-suite failure in packages/api/test/webPushDispatcher.test.ts.

The lease-expiry test now uses a wider real-clock margin while still advancing the fake clock past expiration, eliminating CI timing flakiness without changing production code.

Verification passed:

  • Full dispatcher test file: 17/17
  • Formerly flaky test: 20 consecutive runs
  • API lint
  • API type-check
  • git diff --check

No commit created.


🤖 Implementation Details

  • Model: GPT-5.6 Sol (high)
  • Requested By: @github-actions[bot]
  • Time: 5m 49s
  • Tokens: 2,468,215 (2,459,931 in / 8,284 out)
  • Cost: $1.99
  • 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: 5465654901✓

…ispatcher.test.ts`.

Fixed the full-suite failure in `packages/api/test/webPushDispatcher.test.ts`.

The lease-expiry test now uses a wider real-clock margin while still advancing the fake clock past expiration, eliminating CI timing flakiness without changing production code.

Verification passed:

- Full dispatcher test file: 17/17
- Formerly flaky test: 20 consecutive runs
- API lint
- API type-check
- `git diff --check`

No commit created.

PR: #1993
Comment by: @github-actions[bot] (ID: 5465654901)
Model: gpt-5.6-sol
@github-actions

Copy link
Copy Markdown

Checks Failed

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

View Logs
Build check diagnostics for run 33282785963, 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
c09af8888d6a: Verifying Checksum
c09af8888d6a: Download complete
4ccd7d76ca98: Verifying Checksum
4ccd7d76ca98: 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 ---

added 982 packages, and audited 988 packages in 16s

300 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 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/daemonEventIntake.test.ts test/databaseMigrationGate.test.ts 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/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: 11.708394
      type: 'test'
      ...
    # Subtest: persists a failed state when queue submission fails
    ok 2 - persists a failed state when queue submission fails
      ---
      duration_ms: 1.371625
      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.031643
      type: 'test'
      ...
    # Subtest: times out slow runtime package validation before queueing
    ok 4 - times out slow runtime package validation before queueing
      ---
      duration_ms: 14.316569
      type: 'test'
      ...
    # Subtest: returns package suggestions from configured runtimes
    ok 5 - returns package suggestions from configured runtimes
      ---
      duration_ms: 0.897811
      type: 'test'
      ...
    # Subtest: enforces PROPR_ADMIN_USERS when configured
    ok 6 - enforces PROPR_ADMIN_USERS when configured
      ---
      duration_ms: 0.406952
      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.459231
      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: 3.092154
      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.735677
      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: 6.837011
      type: 'test'
      ...
    # Subtest: resolves the runtime build queue lazily when queueing
    ok 11 - resolves the runtime build queue lazily when queueing
      ---
      duration_ms: 0.950971
      type: 'test'
      ...
    # Subtest: reports apply load failures through the route response
    ok 12 - reports apply load failures through the route response
      ---
      duration_ms: 0.484439
      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: 3.457875
      type: 'test'
      ...
    # Subtest: rejects runtime verification without runtime-management permission
    ok 14 - rejects runtime verification without runtime-management permission
      ---
      duration_ms: 0.390868
      type: 'test'
      ...
    1..14
ok 1 - agent runtime package routes
  ---
  duration_ms: 50.22511
  type: 'suite'
  ...
# [2026-08-30 00:13:32.063 +0000] �[32mINFO�[39m: �[36mCreated data directory for SQLite database�[39m
#     directory: "/home/runner/work/propr/propr/data"
# [2026-08-30 00:13:32.531 +0000] �[32mINFO�[39m: �[36mSQLite database connection established successfully�[39m
#     filename: "/home/runner/work/propr/propr/data/propr.test.sqlite"
#     environment: "test"
# [2026-08-30 00:13:32.574 +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: 11.571587
  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: 6.648087
  type: 'test'
  ...
# Subtest: temporary cleanup refuses paths outside its configured root
ok 4 - temporary cleanup refuses paths outside its configured root
  ---
  duration_ms: 1.078223
  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.798923
  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: 6.850702
  type: 'test'
  ...
# [2026-08-30 00:13:32.562 +0000] �[32mINFO�[39m: �[36mSQLite database connection established successfully�[39m
#     filename: "/home/runner/work/propr/propr/data/propr.test.sqlite"
#     environment: "test"
# [2026-08-30 00:13:32.590 +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: 6.584141
  type: 'test'
  ...
# Subtest: API config subscription reloads repository updates
ok 8 - API config subscription reloads repository updates
  ---
  duration_ms: 0.587384
  type: 'test'
  ...
# Subtest: API config subscription ignores unrelated and malformed events
ok 9 - API config subscription ignores unrelated and malformed events
  ---
  duration_ms: 0.389526
  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: 0.427945
  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: 1.765587
  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.309195
  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.398299
  type: 'test'
  ...
# Subtest: off-tunnel relay inference rejects callbacks outside the exact loopback allowlist
ok 14 - off-tunnel relay inference rejects callbacks outside the exact loopback allowlist
  ---
  duration_ms: 0.461755
  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.216897
  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.204578
  type: 'test'
  ...
# Subtest: explicit custom GitHub web auth remains supported
ok 17 - explicit custom GitHub web auth remains supported
  ---
  duration_ms: 0.248985
  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.230317
  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.728046
  type: 'test'
  ...
# Subtest: redeems a Connect code server-to-server without exposing the relay token in the body
ok 20 - redeems a Connect code server-to-server without exposing the relay token in the body
  ---
  duration_ms: 58.294496
  type: 'test'
  ...
# Subtest: binds the Connect identity username to the validated token owner
ok 21 - binds the Connect identity username to the validated token owner
  ---
  duration_ms: 1.141158
  type: 'test'
  ...
# Subtest: accepts Docker IDs and ProPR-generated container names
ok 22 - accepts Docker IDs and ProPR-generated container names
  ---
  duration_ms: 1.192595
  type: 'test'
  ...
# Subtest: rejects container references that could be parsed as options or shell syntax
ok 23 - rejects container references that could be parsed as options or shell syntax
  ---
  duration_ms: 0.2127
  type: 'test'
  ...
# Subtest: production subprocess call sites do not invoke a command shell
ok 24 - production subprocess call sites do not invoke a command shell
  ---
  duration_ms: 210.601231
  type: 'test'
  ...
# Subtest: no-shell guard rejects aliases and enabled shell options
ok 25 - no-shell guard rejects aliases and enabled shell options
  ---
  duration_ms: 6.62985
  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: 20.539813
      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: 6.581634
      type: 'test'
      ...
    # Subtest: grants full admin permissions to PROPR_ADMIN_USERS
    ok 3 - grants full admin permissions to PROPR_ADMIN_USERS
      ---
      duration_ms: 4.587128
      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: 3.857614
      type: 'test'
      ...
    # Subtest: resolves demo users without installation permissions
    ok 5 - resolves demo users without installation permissions
      ---
      duration_ms: 2.982921
      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: 2.960868
      type: 'test'
      ...
    # Subtest: current-user responses expose capabilities without OAuth credentials
    ok 7 - current-user responses expose capabilities without OAuth credentials
      ---
      duration_ms: 4.945239
      type: 'test'
      ...
    1..7
ok 26 - instance authorization
  ---
  duration_ms: 48.112093
  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.305729
      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: 16.564
      type: 'test'
      ...
    # Subtest: adds explicit members and writes an audit entry
    ok 3 - adds explicit members and writes an audit entry
      ---
      duration_ms: 17.797036
      type: 'test'
      ...
    # Subtest: keeps environment authority separate from durable role assignments
    ok 4 - keeps environment authority separate from durable role assignments
      ---
      duration_ms: 3.85536
      type: 'test'
      ...
    # Subtest: prevents removing the last durable administrator
    ok 5 - prevents removing the last durable administrator
      ---
      duration_ms: 10.865864
      type: 'test'
      ...
    # Subtest: allows role changes once another administrator exists
    ok 6 - allows role changes once another administrator exists
      ---
      duration_ms: 13.66761
      type: 'test'
      ...
    1..6
ok 27 - instance member service
  ---
  duration_ms: 70.569746
  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: 4.487566
      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: 3.580395
      type: 'test'
      ...
    1..2
ok 28 - instance catalog
  ---
  duration_ms: 8.200913
  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: 6.979035
      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: 3.505031
      type: 'test'
      ...
    # Subtest: rejects GitHub usernames with trailing or consecutive hyphens
    ok 3 - rejects GitHub usernames with trailing or consecutive hyphens
      ---
      duration_ms: 3.798775
      type: 'test'
      ...
    1..3
ok 29 - instance admin routes
  ---
  duration_ms: 14.448081
  type: 'suite'
  ...
# [2026-08-30 00:13:35.343 +0000] �[32mINFO�[39m: �[36mSQLite database connection established successfully�[39m
#     filename: "/home/runner/work/propr/propr/data/propr.test.sqlite"
#     environment: "test"
# [2026-08-30 00:13:35.473 +0000] �[32mINFO�[39m: �[36mSQLite database connection closed�[39m
# Subtest: direct API execution binds to loopback by default
ok 30 - direct API execution binds to loopback by default
  ---
  duration_ms: 7.163983
  type: 'test'
  ...
# Subtest: containerized API execution remains reachable through its published port
ok 31 - containerized API execution remains reachable through its published port
  ---
  duration_ms: 0.202775
  type: 'test'
  ...
# Subtest: an explicit API listen host overrides environment defaults
ok 32 - an explicit API listen host overrides environment defaults
  ---
  duration_ms: 0.16592
  type: 'test'
  ...
# Subtest: GitHub OAuth authorization stores and sends a random state nonce
ok 33 - GitHub OAuth authorization stores and sends a random state nonce
  ---
  duration_ms: 3.568677
  type: 'test'
  ...
# Subtest: GitHub OAuth callback rejects a mismatched state before token exchange
ok 34 - GitHub OAuth callback rejects a mismatched state before token exchange
  ---
  duration_ms: 1.710978
  type: 'test'
  ...
# [2026-08-30 00:13:37.198 +0000] �[32mINFO�[39m: �[36mSQLite database connection established successfully�[39m
#     filename: "/home/runner/work/propr/propr/data/propr.test.sqlite"
#     environment: "test"
# [2026-08-30 00:13:37.199 +0000] �[32mINFO�[39m: �[36mSQLite database connection closed�[39m
# Subtest: returns a standard 429 response after the configured quota
ok 35 - returns a standard 429 response after the configured quota
  ---
  duration_ms: 102.797804
  type: 'test'
  ...
# Subtest: does not charge CORS preflight requests against the quota
ok 36 - does not charge CORS preflight requests against the quota
  ---
  duration_ms: 19.660103
  type: 'test'
  ...
# Subtest: route-level webhook limiting preserves alternate-case raw bodies and rejects excess requests before parsing
ok 37 - route-level webhook limiting preserves alternate-case raw bodies and rejects excess requests before parsing
  ---
  duration_ms: 32.801874
  type: 'test'
  ...
# Subtest: does not let an unconfigured private peer rotate quota buckets with X-Forwarded-For
ok 38 - does not let an unconfigured private peer rotate quota buckets with X-Forwarded-For
  ---
  duration_ms: 25.19236
  type: 'test'
  ...
# Subtest: tunnel trust does not let an unrelated private peer rotate quota buckets
ok 39 - tunnel trust does not let an unrelated private peer rotate quota buckets
  ---
  duration_ms: 13.234551
  type: 'test'
  ...
# Subtest: trusted TLS proxy preserves per-client quotas and secure session cookies
ok 40 - trusted TLS proxy preserves per-client quotas and secure session cookies
  ---
  duration_ms: 16.079784
  type: 'test'
  ...
# Subtest: resolves secure defaults and explicit positive-integer overrides
ok 41 - resolves secure defaults and explicit positive-integer overrides
  ---
  duration_ms: 0.31914
  type: 'test'
  ...
# Subtest: rejects invalid overrides instead of silently disabling protection
ok 42 - rejects invalid overrides instead of silently disabling protection
  ---
  duration_ms: 0.53844
  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: 51.050977
      type: 'test'
      ...
    # Subtest: members can read only the sanitized catalog endpoints
    ok 2 - members can read only the sanitized catalog endpoints
      ---
      duration_ms: 54.345255
      type: 'test'
      ...
    # Subtest: administrators pass every management guard in the route matrix
    ok 3 - administrators pass every management guard in the route matrix
      ---
      duration_ms: 26.507829
      type: 'test'
      ...
    1..3
ok 43 - assembled instance permission routes
  ---
  duration_ms: 133.194483
  type: 'suite'
  ...
# [2026-08-30 00:13:37.512 +0000] �[32mINFO�[39m: �[36mSQLite database connection established successfully�[39m
#     filename: "/home/runner/work/propr/propr/data/propr.test.sqlite"
#     environment: "test"
# [2026-08-30 00:13:37.640 +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 44 - /api/status omits disabled configured agents
  ---
  duration_ms: 1778.470937
  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'
# }
# [notifications] Failed to project system health snapshot: Error: notification persistence unavailable
#     at projectSystemSnapshot (/home/runner/work/propr/propr/packages/api/test/statusRoutes.test.ts:227:13)
#     at Object.getStatus (/home/runner/work/propr/propr/packages/api/routes/statusRoutes.ts:185:14)
#     at async readStatus (/home/runner/work/propr/propr/packages/api/test/statusRoutes.test.ts:162:3)
#     at async TestContext.<anonymous> (/home/runner/work/propr/propr/packages/api/test/statusRoutes.test.ts:224: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/compatibility returns public version contract metadata
ok 45 - /api/compatibility returns public version contract metadata
  ---
  duration_ms: 5.189979
  type: 'test'
  ...
# Subtest: /api/status returns default Claude fallback when no agents are configured
ok 46 - /api/status returns default Claude fallback when no agents are configured
  ---
  duration_ms: 2.39981
  type: 'test'
  ...
# Subtest: /api/status isolates system notification projection failures
ok 47 - /api/status isolates system notification projection failures
  ---
  duration_ms: 7.377197
  type: 'test'
  ...
# Subtest: /api/status surfaces unified agent image outages
ok 48 - /api/status surfaces unified agent image outages
  ---
  duration_ms: 3.310166
  type: 'test'
  ...
# Subtest: /api/status includes warnings field in demo mode
ok 49 - /api/status includes warnings field in demo mode
  ---
  duration_ms: 1.440959
  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 caches agent health checks briefly
ok 50 - /api/status caches agent health checks briefly
  ---
  duration_ms: 4.000771
  type: 'test'
  ...
# Subtest: /api/status reports resolved auth mode and event intake mode
ok 51 - /api/status reports resolved auth mode and event intake mode
  ---
  duration_ms: 3.433587
  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 52 - /api/status reports connected intake status when routing state is live
  ---
  duration_ms: 9.907889
  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 reports disconnected intake status when routing state is down
ok 53 - /api/status reports disconnected intake status when routing state is down
  ---
  duration_ms: 11.280661
  type: 'test'
  ...
# Subtest: /api/status reports active intake status for polling when the daemon is running
ok 54 - /api/status reports active intake status for polling when the daemon is running
  ---
  duration_ms: 2.484086
  type: 'test'
  ...
# Subtest: /api/status reports disconnected intake status for polling when the daemon is stopped
ok 55 - /api/status reports disconnected intake status for polling when the daemon is stopped
  ---
  duration_ms: 5.455912
  type: 'test'
  ...
# Subtest: /api/status includes routing state published by the daemon
ok 56 - /api/status includes routing state published by the daemon
  ---
  duration_ms: 1.802396
  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 exposes only validated UI-safe Connect account fields
ok 57 - /api/status exposes only validated UI-safe Connect account fields
  ---
  duration_ms: 11.852445
  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 rejects impossible account dates and preserves valid leap-day instants
ok 58 - /api/status rejects impossible account dates and preserves valid leap-day instants
  ---
  duration_ms: 15.184949
  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 drops malformed or disconnected Connect account state without assuming Community
ok 59 - /api/status drops malformed or disconnected Connect account state without assuming Community
  ---
  duration_ms: 2.856005
  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 does not expose Connect account state for a non-Connect intake mode
ok 60 - /api/status does not expose Connect account state for a non-Connect intake mode
  ---
  duration_ms: 4.761271
  type: 'test'
  ...
# Subtest: /api/status reports connected githubAuth for relay-auth deployments
ok 61 - /api/status reports connected githubAuth for relay-auth deployments
  ---
  duration_ms: 1.636986
  type: 'test'
  ...
# [2026-08-30 00:13:38.478 +0000] �[32mINFO�[39m: �[36mSQLite database connection established successfully�[39m
#     filename: "/home/runner/work/propr/propr/data/propr.test.sqlite"
#     environment: "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 reports unknown auth mode and disconnected health when the resolver is bypassed
ok 62 - /api/status reports unknown auth mode and disconnected health when the resolver is bypassed
  ---
  duration_ms: 5.531245
  type: 'test'
  ...
# Subtest: /api/status omits malformed routing state
ok 63 - /api/status omits malformed routing state
  ---
  duration_ms: 1.58712
  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 omits routing state with a malformed lastAckAt timestamp
ok 64 - /api/status omits routing state with a malformed lastAckAt timestamp
  ---
  duration_ms: 4.66816
  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 omits routing state when none is published
ok 65 - /api/status omits routing state when none is published
  ---
  duration_ms: 1.527921
  type: 'test'
  ...
# Subtest: /api/status reports demo auth mode in demo mode
ok 66 - /api/status reports demo auth mode in demo mode
  ---
  duration_ms: 3.469127
  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

...(truncated)

View Workflow

@propr-dev

propr-dev Bot commented Aug 30, 2026

Copy link
Copy Markdown
Contributor Author

Applied the requested follow-up changes in commit 3b965be

Summary of Changes

Fixed the flaky Web Push pagination regression in webPushDispatcher.test.ts.

Changes:

  • Made fixture timestamps monotonic, guaranteeing the intended 21-row quiet-hours prefix.
  • Increased the test claim lease margin to avoid CI clock sensitivity.
  • Production code remains unchanged.

Verification passed:

  • Formerly failing test: 10 consecutive runs
  • Notification server suite: 9/9 files
  • Notification UI suite: 15 files, 86 tests
  • API lint and type-check
  • git diff --check

Only the intended test file is modified; no commit was created.


🤖 Implementation Details

  • Model: GPT-5.6 Sol (high)
  • Requested By: @github-actions[bot]
  • Time: 11m 58s
  • Tokens: 3,272,871 (3,260,965 in / 11,906 out)
  • Cost: $2.53
  • 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: 5465690735✓

…ispatcher.test.ts](/tmp/git-processor/worktrees/integry/propr/pr-1993-followup-2026-08-30T00-15-15/packages/api/test/webPushDispatcher.test.ts:272).

Fixed the flaky Web Push pagination regression in [webPushDispatcher.test.ts](/tmp/git-processor/worktrees/integry/propr/pr-1993-followup-2026-08-30T00-15-15/packages/api/test/webPushDispatcher.test.ts:272).

Changes:

- Made fixture timestamps monotonic, guaranteeing the intended 21-row quiet-hours prefix.
- Increased the test claim lease margin to avoid CI clock sensitivity.
- Production code remains unchanged.

Verification passed:

- Formerly failing test: 10 consecutive runs
- Notification server suite: 9/9 files
- Notification UI suite: 15 files, 86 tests
- API lint and type-check
- `git diff --check`

Only the intended test file is modified; no commit was created.

PR: #1993
Comment by: @github-actions[bot] (ID: 5465690735)
Model: gpt-5.6-sol
@propr-dev

propr-dev Bot commented Aug 30, 2026

Copy link
Copy Markdown
Contributor Author

AI Code Review Complete requested by @propr-ultrafix

Posted 1 review:

View Task Details

@propr-dev

propr-dev Bot commented Aug 30, 2026

Copy link
Copy Markdown
Contributor Author

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

Overall Evaluation

This PR cleanly introduces persisted synthetic-agent contracts, management APIs, reference validation, and catalog projection. It needs minor changes before merge because synthetic defaults are not preserved during direct-agent updates, and top-level synthetic IDs are not checked for uniqueness.

Strong configuration boundaries — Strict Zod schemas validate formats, defaults, ranges, nested duplicates, and direct-agent model references.

Safe configuration workflow — Synthetic and direct updates share the configuration lock, with post-commit publication handling and appropriate authorization.

Focused verification — Persistence, API validation, deletion protection, catalog filtering, and authorization are covered, and all current-head checks pass.

Merge blockers

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

F1: 🔴 Direct updates overwrite synthetic defaults

  • Required behavior: A valid synthetic agent exposed as the configured default must remain the default when an unrelated direct-agent configuration is updated.
  • Evidence: packages/api/routes/configRoutesAgents.ts:239 — after loading synthetic agents, the code still calls resolveDefaultAgentAlias(processedAgents, currentDefault) with only direct agents; consequently, any synthetic currentDefault is replaced by the first enabled direct alias.
  • Minimum fix: Include operational synthetic aliases when resolving whether currentDefault remains valid, and add a regression test showing that a direct-agent update preserves a valid synthetic default.

F2: 🔴 Duplicate synthetic agent IDs accepted

  • Required behavior: The new id field represents stable configuration identity, so distinct top-level synthetic agents must not be accepted with the same ID.
  • Evidence: packages/shared/src/syntheticAgents.ts:91 — the collection-level refinement detects duplicate aliases only; two agents with different aliases and the same UUID pass validation and are emitted in the catalog with an ambiguous identity.
  • Minimum fix: Track top-level agent IDs in syntheticAgentConfigsSchema, report duplicate IDs with an indexed path, and add a schema test for this case.

Suggestions

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

S1: 🟢 Reconcile removed synthetic defaults

When a synthetic agent that currently supplies default_agent_alias is disabled or removed, the POST route leaves that persisted setting stale. The catalog currently suppresses an unavailable default and safely falls back, so this need not block the present change, but rejecting the update or atomically selecting another default would keep stored configuration internally consistent.

Score

The implementation is well structured, well tested, and has fully passing checks, but two localized correctness gaps remain in identity validation and default-agent handling.

Score: 6/10


🤖 Review Details

  • Model: GPT-5.6 Sol
  • Time: 3m 49s
  • Tokens: 61,792 (53,437 in / 8,355 out)
  • Cost: $0.47

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 Aug 30, 2026

Copy link
Copy Markdown
Contributor Author

Applied the requested follow-up changes in commit 9126283

Addressed 1 AI review comment (IDs: 5465803260)

Summary of Changes

Implemented F1 and F2 without committing.

Verification passed:

  • 9 focused tests
  • Shared and API type-checks
  • API lint
  • git diff --check

🤖 Implementation Details

  • Model: GPT-5.6 Sol (high)
  • Requested By: @propr-ultrafix
  • Time: 4m 5s
  • Tokens: 594,724 (587,380 in / 7,344 out)
  • Cost: $0.73

View 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

Implemented F1 and F2 without committing.

- Direct-agent updates now preserve an operational synthetic default in [configRoutesAgents.ts](/tmp/git-processor/worktrees/integry/propr/pr-1993-followup-2026-08-30T00-42-51/packages/api/routes/configRoutesAgents.ts).
- Duplicate top-level synthetic agent IDs now produce an indexed validation error in [syntheticAgents.ts](/tmp/git-processor/worktrees/integry/propr/pr-1993-followup-2026-08-30T00-42-51/packages/shared/src/syntheticAgents.ts).
- Added both regression tests in [syntheticAgents.test.ts](/tmp/git-processor/worktrees/integry/propr/pr-1993-followup-2026-08-30T00-42-51/packages/api/test/syntheticAgents.test.ts).

Verification passed:

- 9 focused tests
- Shared and API type-checks
- API lint
- `git diff --check`

PR: #1993
Comment by: @propr-ultrafix (ID: 0)
Model: gpt-5.6-sol
@propr-dev

propr-dev Bot commented Aug 30, 2026

Copy link
Copy Markdown
Contributor Author

AI Code Review Complete requested by @propr-ultrafix

Posted 1 review:

View Task Details

@propr-dev

propr-dev Bot commented Aug 30, 2026

Copy link
Copy Markdown
Contributor Author

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

Overall Evaluation

The PR establishes strong persisted contracts, validation, management endpoints, referential checks, and catalog projection for synthetic agents. However, it exposes synthetic agents as operational choices without adding runtime resolution, and synthetic updates can invalidate the configured default. It therefore needs significant rework before merge.

Strong shared validation — The Zod schemas enforce identifiers, defaults, uniqueness, model membership, priorities, usage limits, and cross-references with actionable errors.

Careful direct-agent integrity — Direct-agent deletion and modification are checked against synthetic references under the shared configuration lock.

Good focused coverage — Persistence, API validation, authorization, catalog filtering, and direct-agent interactions are tested, and all current checks pass.

Merge blockers

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

F3: 🔴 Synthetic selections cannot execute

  • Required behavior: Agents exposed through the operational instance catalog, including the configured default, must resolve to executable runtime agents. The PR advertises synthetic aliases and preserves them as defaults, but the runtime registry remains direct-agent-only.
  • Evidence: packages/api/routes/instanceCatalogRoutes.ts:35catalogSyntheticAgents publishes usable synthetic aliases and models as enabled catalog entries; packages/api/routes/configRoutesAgents.ts:253 — an operational synthetic alias is accepted as newDefault and subsequently installed in the live registry, despite no synthetic runtime resolver being introduced.
  • Minimum fix: Resolve each synthetic alias/model to an eligible direct member before runtime registry lookup or dispatch, honoring the declared selection strategy and eligibility constraints. Ensure all default-agent paths use that resolver; alternatively, do not expose or preserve synthetic aliases as operational choices until this support exists.

F4: 🔴 Synthetic updates can orphan the default

  • Required behavior: A successful configuration replacement must not leave default_agent_alias pointing to a removed, disabled, or non-operational agent.
  • Evidence: packages/api/routes/configRoutesSyntheticAgents.ts:70 — the POST handler validates references and replaces synthetic_agents, but never loads or reconciles settings before committing; this permits an empty array or a disabled/non-operational replacement to remove the current synthetic default.
  • Minimum fix: Under the existing settings lock, either reject an update that makes the configured synthetic default non-operational or atomically select and persist a valid fallback, including the corresponding settings notification and live-state synchronization.

Suggestions

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

No suggestions.

Score

The contracts and focused tests are solid, and all current checks pass, but the advertised operational behavior is disconnected from runtime execution and default integrity is not maintained.

Score: 5/10


🤖 Review Details

  • Model: GPT-5.6 Sol
  • Time: 1m 39s
  • Tokens: 56,191 (52,460 in / 3,731 out)
  • Cost: $0.33

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 Aug 30, 2026

Copy link
Copy Markdown
Contributor Author

Applied the requested follow-up changes in commit 8785a41

Addressed 1 AI review comment (IDs: 5465875568)

Summary of Changes

Implemented F3 and F4 using the review-approved compatibility path:

  • Synthetic agents remain configurable but are excluded from the operational instance catalog.
  • Direct-agent updates replace synthetic defaults with an executable direct default, persist the setting, publish settings_update, and synchronize live registry state.
  • Synthetic configuration updates are rejected under the settings lock while the configured default is synthetic, preventing orphaned defaults.
  • Added regression coverage for catalog filtering, fallback synchronization, and rejected synthetic replacements.

Verification passed:

  • API and root TypeScript checks
  • API lint
  • 28 focused tests
  • git diff --check

Changes are uncommitted as requested.


🤖 Implementation Details

  • Model: GPT-5.6 Sol (high)
  • Requested By: @propr-ultrafix
  • Time: 6m 45s
  • Tokens: 2,413,053 (2,399,469 in / 13,584 out)
  • Cost: $2.09

View 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

…ty path:

Implemented F3 and F4 using the review-approved compatibility path:

- Synthetic agents remain configurable but are excluded from the operational instance catalog.
- Direct-agent updates replace synthetic defaults with an executable direct default, persist the setting, publish `settings_update`, and synchronize live registry state.
- Synthetic configuration updates are rejected under the settings lock while the configured default is synthetic, preventing orphaned defaults.
- Added regression coverage for catalog filtering, fallback synchronization, and rejected synthetic replacements.

Verification passed:

- API and root TypeScript checks
- API lint
- 28 focused tests
- `git diff --check`

Changes are uncommitted as requested.

PR: #1993
Comment by: @propr-ultrafix (ID: 0)
Model: gpt-5.6-sol
@propr-dev

propr-dev Bot commented Aug 30, 2026

Copy link
Copy Markdown
Contributor Author

AI Code Review Complete requested by @propr-ultrafix

Posted 1 review:

View Task Details

@propr-dev

propr-dev Bot commented Aug 30, 2026

Copy link
Copy Markdown
Contributor Author

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

Overall Evaluation

This PR builds a solid persistence and validation layer for synthetic agents, but it is not ready to merge and needs significant changes in catalog exposure and default-agent integrity.

Strong configuration contracts — The strict Zod schemas validate identifiers, defaults, duplicate members/models/aliases, priorities, percentages, and physical agent-model references.

Safe configuration updates — Synthetic and direct-agent mutations share the settings lock, preserve committed-write semantics, and protect referenced direct agents from deletion.

Good focused coverage — Persistence, validation, authorization, configuration APIs, deletion protection, and catalog behavior are tested, and all current checks pass.

Merge blockers

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

F5: 🔴 Synthetic agents missing from catalog

  • Required behavior: The immutable objective explicitly requires synthetic agents to be added to /api/instance/catalog while retaining /api/catalog compatibility.
  • Evidence: packages/api/routes/instanceCatalogRoutes.ts:89 — the changed implementation explicitly limits the catalog to direct agents loaded through loadAgents() and never loads or projects loadSyntheticAgents(); the added test consequently asserts that synthetic aliases are absent.
  • Minimum fix: Load synthetic configuration for /api/instance/catalog and project enabled entries using kind: 'synthetic', their stable ID, enabled model IDs, and default model. Preserve the intended legacy /api/catalog behavior, using separate handlers if its compatibility contract differs.

F6: 🔴 Synthetic defaults remain writable

  • Required behavior: Because synthetic agents are explicitly non-executable in this implementation, configuration mutations must not establish a synthetic alias as default_agent_alias.
  • Evidence: packages/api/routes/configRoutesSyntheticAgents.ts:84 — the added guard recognizes that synthetic defaults cannot execute, but determines this only from previousSyntheticAgents. It does not inspect the proposed synthetic set and does not protect the settings mutation boundary, which can subsequently assign an existing synthetic alias as the default.
  • Minimum fix: Reject or transactionally replace a configured default when it occurs in either the previous or proposed synthetic set, and validate default_agent_alias settings updates so they cannot select synthetic aliases until synthetic runtime execution is implemented.

Suggestions

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

S1: 🟢 Remove unused alias parameter

additionalEnabledAliases in packages/api/routes/configRoutesAgentsPreparation.ts:24 has no changed caller and conflicts conceptually with the current direct-only runtime policy. Removing it until synthetic defaults are executable would make the intended behavior clearer, but it does not independently cause incorrect runtime behavior.

Score

The validation, persistence, locking, and test work are strong, and all current checks pass. However, a stated catalog requirement is missing and the new configuration namespace can still produce a non-executable default.

Score: 5/10


🤖 Review Details

  • Model: GPT-5.6 Sol
  • Time: 3m 28s
  • Tokens: 60,905 (51,255 in / 9,650 out)
  • Cost: $0.50

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 Aug 30, 2026

Copy link
Copy Markdown
Contributor Author

Applied the requested follow-up changes in commit ba1be48

Addressed 1 AI review comment (IDs: 5465970674)

Summary of Changes

Implemented F5 and F6 only; changes remain uncommitted.

  • /api/instance/catalog now includes enabled synthetic agents with stable IDs, kind: 'synthetic', enabled model IDs, and defaults.
  • Legacy /api/catalog remains direct-agent-only.
  • Synthetic configuration updates reject defaults found in either previous or proposed synthetic sets.
  • Settings updates cannot assign a synthetic alias as default_agent_alias.

Key files: instanceCatalogRoutes.ts, configRoutes.ts, configRoutesSyntheticAgents.ts.

Verification passed:

  • API and root TypeScript checks
  • 32 focused tests
  • 100 broader configuration-route tests
  • git diff --check
  • API lint: no errors; two pre-existing oversized-file warnings

🤖 Implementation Details

  • Model: GPT-5.6 Sol (high)
  • Requested By: @propr-ultrafix
  • Time: 6m 3s
  • Tokens: 1,777,263 (1,766,608 in / 10,655 out)
  • Cost: $1.75

View 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

Implemented F5 and F6 only; changes remain uncommitted.

- `/api/instance/catalog` now includes enabled synthetic agents with stable IDs, `kind: 'synthetic'`, enabled model IDs, and defaults.
- Legacy `/api/catalog` remains direct-agent-only.
- Synthetic configuration updates reject defaults found in either previous or proposed synthetic sets.
- Settings updates cannot assign a synthetic alias as `default_agent_alias`.

Key files: [instanceCatalogRoutes.ts](/tmp/git-processor/worktrees/integry/propr/pr-1993-followup-2026-08-30T01-23-14/packages/api/routes/instanceCatalogRoutes.ts:39), [configRoutes.ts](/tmp/git-processor/worktrees/integry/propr/pr-1993-followup-2026-08-30T01-23-14/packages/api/routes/configRoutes.ts:321), [configRoutesSyntheticAgents.ts](/tmp/git-processor/worktrees/integry/propr/pr-1993-followup-2026-08-30T01-23-14/packages/api/routes/configRoutesSyntheticAgents.ts:81).

Verification passed:

- API and root TypeScript checks
- 32 focused tests
- 100 broader configuration-route tests
- `git diff --check`
- API lint: no errors; two pre-existing oversized-file warnings

PR: #1993
Comment by: @propr-ultrafix (ID: 0)
Model: gpt-5.6-sol
@github-actions

Copy link
Copy Markdown

Checks Failed

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

View Logs
Build check diagnostics for run 33285788574, 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
c09af8888d6a: Verifying Checksum
c09af8888d6a: Download complete
4ccd7d76ca98: Verifying Checksum
4ccd7d76ca98: 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 ---

added 982 packages, and audited 988 packages in 14s

300 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 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/daemonEventIntake.test.ts test/databaseMigrationGate.test.ts 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/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: 4.351733
      type: 'test'
      ...
    # Subtest: persists a failed state when queue submission fails
    ok 2 - persists a failed state when queue submission fails
      ---
      duration_ms: 0.905224
      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: 0.562504
      type: 'test'
      ...
    # Subtest: times out slow runtime package validation before queueing
    ok 4 - times out slow runtime package validation before queueing
      ---
      duration_ms: 5.563315
      type: 'test'
      ...
    # Subtest: returns package suggestions from configured runtimes
    ok 5 - returns package suggestions from configured runtimes
      ---
      duration_ms: 2.754168
      type: 'test'
      ...
    # Subtest: enforces PROPR_ADMIN_USERS when configured
    ok 6 - enforces PROPR_ADMIN_USERS when configured
      ---
      duration_ms: 0.317106
      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.391986
      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.392333
      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.550357
      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: 2.239726
      type: 'test'
      ...
    # Subtest: resolves the runtime build queue lazily when queueing
    ok 11 - resolves the runtime build queue lazily when queueing
      ---
      duration_ms: 1.659851
      type: 'test'
      ...
    # Subtest: reports apply load failures through the route response
    ok 12 - reports apply load failures through the route response
      ---
      duration_ms: 0.38885
      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.572395
      type: 'test'
      ...
    # Subtest: rejects runtime verification without runtime-management permission
    ok 14 - rejects runtime verification without runtime-management permission
      ---
      duration_ms: 0.267149
      type: 'test'
      ...
    1..14
ok 1 - agent runtime package routes
  ---
  duration_ms: 22.504421
  type: 'suite'
  ...
# [2026-08-30 01:30:13.114 +0000] �[32mINFO�[39m: �[36mCreated data directory for SQLite database�[39m
#     directory: "/home/runner/work/propr/propr/data"
# [2026-08-30 01:30:13.481 +0000] �[32mINFO�[39m: �[36mSQLite database connection established successfully�[39m
#     filename: "/home/runner/work/propr/propr/data/propr.test.sqlite"
#     environment: "test"
# [2026-08-30 01:30:13.500 +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: 17.895485
  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: 9.190568
  type: 'test'
  ...
# Subtest: temporary cleanup refuses paths outside its configured root
ok 4 - temporary cleanup refuses paths outside its configured root
  ---
  duration_ms: 0.633417
  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.481061
  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: 4.625059
  type: 'test'
  ...
# [2026-08-30 01:30:13.505 +0000] �[32mINFO�[39m: �[36mSQLite database connection established successfully�[39m
#     filename: "/home/runner/work/propr/propr/data/propr.test.sqlite"
#     environment: "test"
# [2026-08-30 01:30:13.536 +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: 6.086636
  type: 'test'
  ...
# Subtest: API config subscription reloads repository updates
ok 8 - API config subscription reloads repository updates
  ---
  duration_ms: 0.510165
  type: 'test'
  ...
# Subtest: API config subscription ignores unrelated and malformed events
ok 9 - API config subscription ignores unrelated and malformed events
  ---
  duration_ms: 0.297006
  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: 0.515021
  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: 1.059212
  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.243068
  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.239469
  type: 'test'
  ...
# Subtest: off-tunnel relay inference rejects callbacks outside the exact loopback allowlist
ok 14 - off-tunnel relay inference rejects callbacks outside the exact loopback allowlist
  ---
  duration_ms: 0.241677
  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.104264
  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.097512
  type: 'test'
  ...
# Subtest: explicit custom GitHub web auth remains supported
ok 17 - explicit custom GitHub web auth remains supported
  ---
  duration_ms: 0.139618
  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.121744
  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.431187
  type: 'test'
  ...
# Subtest: redeems a Connect code server-to-server without exposing the relay token in the body
ok 20 - redeems a Connect code server-to-server without exposing the relay token in the body
  ---
  duration_ms: 34.996551
  type: 'test'
  ...
# Subtest: binds the Connect identity username to the validated token owner
ok 21 - binds the Connect identity username to the validated token owner
  ---
  duration_ms: 2.924556
  type: 'test'
  ...
# Subtest: accepts Docker IDs and ProPR-generated container names
ok 22 - accepts Docker IDs and ProPR-generated container names
  ---
  duration_ms: 0.767872
  type: 'test'
  ...
# Subtest: rejects container references that could be parsed as options or shell syntax
ok 23 - rejects container references that could be parsed as options or shell syntax
  ---
  duration_ms: 0.158398
  type: 'test'
  ...
# Subtest: production subprocess call sites do not invoke a command shell
ok 24 - production subprocess call sites do not invoke a command shell
  ---
  duration_ms: 88.606376
  type: 'test'
  ...
# Subtest: no-shell guard rejects aliases and enabled shell options
ok 25 - no-shell guard rejects aliases and enabled shell options
  ---
  duration_ms: 3.257612
  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: 17.922434
      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: 8.546259
      type: 'test'
      ...
    # Subtest: grants full admin permissions to PROPR_ADMIN_USERS
    ok 3 - grants full admin permissions to PROPR_ADMIN_USERS
      ---
      duration_ms: 3.19098
      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: 2.917261
      type: 'test'
      ...
    # Subtest: resolves demo users without installation permissions
    ok 5 - resolves demo users without installation permissions
      ---
      duration_ms: 2.284352
      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.277013
      type: 'test'
      ...
    # Subtest: current-user responses expose capabilities without OAuth credentials
    ok 7 - current-user responses expose capabilities without OAuth credentials
      ---
      duration_ms: 3.764233
      type: 'test'
      ...
    1..7
ok 26 - instance authorization
  ---
  duration_ms: 44.153497
  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: 3.468208
      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: 10.215525
      type: 'test'
      ...
    # Subtest: adds explicit members and writes an audit entry
    ok 3 - adds explicit members and writes an audit entry
      ---
      duration_ms: 3.560925
      type: 'test'
      ...
    # Subtest: keeps environment authority separate from durable role assignments
    ok 4 - keeps environment authority separate from durable role assignments
      ---
      duration_ms: 2.696982
      type: 'test'
      ...
    # Subtest: prevents removing the last durable administrator
    ok 5 - prevents removing the last durable administrator
      ---
      duration_ms: 6.643883
      type: 'test'
      ...
    # Subtest: allows role changes once another administrator exists
    ok 6 - allows role changes once another administrator exists
      ---
      duration_ms: 5.383894
      type: 'test'
      ...
    1..6
ok 27 - instance member service
  ---
  duration_ms: 32.339984
  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: 2.336052
      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: 1.782817
      type: 'test'
      ...
    1..2
ok 28 - instance catalog
  ---
  duration_ms: 4.247197
  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: 8.486002
      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: 1.29689
      type: 'test'
      ...
    # Subtest: rejects GitHub usernames with trailing or consecutive hyphens
    ok 3 - rejects GitHub usernames with trailing or consecutive hyphens
      ---
      duration_ms: 2.697713
      type: 'test'
      ...
    1..3
ok 29 - instance admin routes
  ---
  duration_ms: 12.601279
  type: 'suite'
  ...
# [2026-08-30 01:30:15.225 +0000] �[32mINFO�[39m: �[36mSQLite database connection established successfully�[39m
#     filename: "/home/runner/work/propr/propr/data/propr.test.sqlite"
#     environment: "test"
# [2026-08-30 01:30:15.310 +0000] �[32mINFO�[39m: �[36mSQLite database connection closed�[39m
# Subtest: direct API execution binds to loopback by default
ok 30 - direct API execution binds to loopback by default
  ---
  duration_ms: 0.664881
  type: 'test'
  ...
# Subtest: containerized API execution remains reachable through its published port
ok 31 - containerized API execution remains reachable through its published port
  ---
  duration_ms: 0.134698
  type: 'test'
  ...
# Subtest: an explicit API listen host overrides environment defaults
ok 32 - an explicit API listen host overrides environment defaults
  ---
  duration_ms: 0.107891
  type: 'test'
  ...
# Subtest: GitHub OAuth authorization stores and sends a random state nonce
ok 33 - GitHub OAuth authorization stores and sends a random state nonce
  ---
  duration_ms: 2.552193
  type: 'test'
  ...
# Subtest: GitHub OAuth callback rejects a mismatched state before token exchange
ok 34 - GitHub OAuth callback rejects a mismatched state before token exchange
  ---
  duration_ms: 1.239776
  type: 'test'
  ...
# [2026-08-30 01:30:16.345 +0000] �[32mINFO�[39m: �[36mSQLite database connection established successfully�[39m
#     filename: "/home/runner/work/propr/propr/data/propr.test.sqlite"
#     environment: "test"
# [2026-08-30 01:30:16.345 +0000] �[32mINFO�[39m: �[36mSQLite database connection closed�[39m
# Subtest: returns a standard 429 response after the configured quota
ok 35 - returns a standard 429 response after the configured quota
  ---
  duration_ms: 71.918596
  type: 'test'
  ...
# Subtest: does not charge CORS preflight requests against the quota
ok 36 - does not charge CORS preflight requests against the quota
  ---
  duration_ms: 12.110657
  type: 'test'
  ...
# Subtest: route-level webhook limiting preserves alternate-case raw bodies and rejects excess requests before parsing
ok 37 - route-level webhook limiting preserves alternate-case raw bodies and rejects excess requests before parsing
  ---
  duration_ms: 11.628276
  type: 'test'
  ...
# Subtest: does not let an unconfigured private peer rotate quota buckets with X-Forwarded-For
ok 38 - does not let an unconfigured private peer rotate quota buckets with X-Forwarded-For
  ---
  duration_ms: 9.487936
  type: 'test'
  ...
# Subtest: tunnel trust does not let an unrelated private peer rotate quota buckets
ok 39 - tunnel trust does not let an unrelated private peer rotate quota buckets
  ---
  duration_ms: 8.65978
  type: 'test'
  ...
# Subtest: trusted TLS proxy preserves per-client quotas and secure session cookies
ok 40 - trusted TLS proxy preserves per-client quotas and secure session cookies
  ---
  duration_ms: 19.851449
  type: 'test'
  ...
# Subtest: resolves secure defaults and explicit positive-integer overrides
ok 41 - resolves secure defaults and explicit positive-integer overrides
  ---
  duration_ms: 0.338596
  type: 'test'
  ...
# Subtest: rejects invalid overrides instead of silently disabling protection
ok 42 - rejects invalid overrides instead of silently disabling protection
  ---
  duration_ms: 0.535289
  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: 24.074775
      type: 'test'
      ...
    # Subtest: members can read only the sanitized catalog endpoints
    ok 2 - members can read only the sanitized catalog endpoints
      ---
      duration_ms: 50.35698
      type: 'test'
      ...
    # Subtest: administrators pass every management guard in the route matrix
    ok 3 - administrators pass every management guard in the route matrix
      ---
      duration_ms: 27.193074
      type: 'test'
      ...
    1..3
ok 43 - assembled instance permission routes
  ---
  duration_ms: 102.44954
  type: 'suite'
  ...
# [2026-08-30 01:30:16.577 +0000] �[32mINFO�[39m: �[36mSQLite database connection established successfully�[39m
#     filename: "/home/runner/work/propr/propr/data/propr.test.sqlite"
#     environment: "test"
# [2026-08-30 01:30:16.677 +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 44 - /api/status omits disabled configured agents
  ---
  duration_ms: 1227.056759
  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'
# }
# [notifications] Failed to project system health snapshot: Error: notification persistence unavailable
#     at projectSystemSnapshot (/home/runner/work/propr/propr/packages/api/test/statusRoutes.test.ts:227:13)
#     at Object.getStatus (/home/runner/work/propr/propr/packages/api/routes/statusRoutes.ts:185:14)
#     at async readStatus (/home/runner/work/propr/propr/packages/api/test/statusRoutes.test.ts:162:3)
#     at async TestContext.<anonymous> (/home/runner/work/propr/propr/packages/api/test/statusRoutes.test.ts:224: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/compatibility returns public version contract metadata
ok 45 - /api/compatibility returns public version contract metadata
  ---
  duration_ms: 4.416317
  type: 'test'
  ...
# Subtest: /api/status returns default Claude fallback when no agents are configured
ok 46 - /api/status returns default Claude fallback when no agents are configured
  ---
  duration_ms: 1.977962
  type: 'test'
  ...
# Subtest: /api/status isolates system notification projection failures
ok 47 - /api/status isolates system notification projection failures
  ---
  duration_ms: 5.99368
  type: 'test'
  ...
# Subtest: /api/status surfaces unified agent image outages
ok 48 - /api/status surfaces unified agent image outages
  ---
  duration_ms: 17.137406
  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'
# }
# 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'
# }
# 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 warnings field in demo mode
ok 49 - /api/status includes warnings field in demo mode
  ---
  duration_ms: 3.617033
  type: 'test'
  ...
# Subtest: /api/status caches agent health checks briefly
ok 50 - /api/status caches agent health checks briefly
  ---
  duration_ms: 2.815104
  type: 'test'
  ...
# Subtest: /api/status reports resolved auth mode and event intake mode
ok 51 - /api/status reports resolved auth mode and event intake mode
  ---
  duration_ms: 1.411657
  type: 'test'
  ...
# Subtest: /api/status reports connected intake status when routing state is live
ok 52 - /api/status reports connected intake status when routing state is live
  ---
  duration_ms: 1.86413
  type: 'test'
  ...
# Subtest: /api/status reports disconnected intake status when routing state is down
ok 53 - /api/status reports disconnected intake status when routing state is down
  ---
  duration_ms: 2.226776
  type: 'test'
  ...
# Subtest: /api/status reports active intake status for polling when the daemon is running
ok 54 - /api/status reports active intake status for polling when the daemon is running
  ---
  duration_ms: 1.748871
  type: 'test'
  ...
# Subtest: /api/status reports disconnected intake status for polling when the daemon is stopped
ok 55 - /api/status reports disconnected intake status for polling when the daemon is stopped
  ---
  duration_ms: 2.967363
  type: 'test'
  ...
# Subtest: /api/status includes routing state published by the daemon
ok 56 - /api/status includes routing state published by the daemon
  ---
  duration_ms: 1.360364
  type: 'test'
  ...
# Subtest: /api/status exposes only validated UI-safe Connect account fields
ok 57 - /api/status exposes only validated UI-safe Connect account fields
  ---
  duration_ms: 1.593373
  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'
# }
# 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'
# }
# 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 rejects impossible account dates and preserves valid leap-day instants
ok 58 - /api/status rej

...(truncated)

View Workflow

@propr-dev

propr-dev Bot commented Aug 30, 2026

Copy link
Copy Markdown
Contributor Author

Applied the requested follow-up changes in commit e55cf30

Summary of Changes

Fixed the PR #1993 build-check failure without committing.

Changes:

Verification passed:

  • API lint with --max-warnings 0
  • API TypeScript build
  • All 11 focused synthetic-agent tests
  • git diff --check

🤖 Implementation Details

  • Model: GPT-5.6 Sol (high)
  • Requested By: @github-actions[bot]
  • Time: 5m 44s
  • Tokens: 1,545,704 (1,536,074 in / 9,630 out)
  • Cost: $1.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 ID: 5466014380✓

Fixed the PR #1993 build-check failure without committing.

Changes:

- Reduced `configRoutes.ts` below ESLint’s 400-line limit.
- Split contract tests into [syntheticAgentContracts.test.ts](/tmp/git-processor/worktrees/integry/propr/pr-1993-followup-2026-08-30T01-33-24/packages/api/test/syntheticAgentContracts.test.ts), preserving all test coverage.
- Reduced [syntheticAgents.test.ts](/tmp/git-processor/worktrees/integry/propr/pr-1993-followup-2026-08-30T01-33-24/packages/api/test/syntheticAgents.test.ts) below the limit.

Verification passed:

- API lint with `--max-warnings 0`
- API TypeScript build
- All 11 focused synthetic-agent tests
- `git diff --check`

PR: #1993
Comment by: @github-actions[bot] (ID: 5466014380)
Model: gpt-5.6-sol
@propr-dev

propr-dev Bot commented Aug 30, 2026

Copy link
Copy Markdown
Contributor Author

AI Code Review Complete requested by @propr-ultrafix

Posted 1 review:

View Task Details

@propr-dev

propr-dev Bot commented Aug 30, 2026

Copy link
Copy Markdown
Contributor Author

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

Overall Evaluation

This PR is ready to merge. It adds persisted synthetic-agent contracts, guarded configuration endpoints, cross-configuration integrity checks, and a backward-compatible catalog path with strong focused coverage.

Strong validation boundaries — Shared Zod schemas enforce defaults, uniqueness, model/member structure, and direct-agent references.

Safe configuration updates — Synthetic, direct-agent, and default-setting mutations share the settings lock, with atomic persistence and explicit publication-failure handling.

Compatibility preserved/api/instance/catalog exposes synthetic agents while /api/catalog retains direct-agent-only behavior, and all current-head checks pass.

Merge blockers

No merge blockers.

Suggestions

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

S1: 🟢 Surface disable warnings

When a direct-agent update disables the last executable member of an enabled synthetic model, validateDirectAgentUpdateIntegrity in packages/api/routes/configRoutesAgents.ts permits the change but discards the reference validator’s warnings. Returning those warnings alongside existing CLI compatibility warnings would improve administrator feedback, though it is optional because disabling referenced agents is intentionally supported and referential integrity remains intact.

Score

The implementation is coherent, well tested, and merge-ready, with no verified correctness or safety blockers and all authoritative checks passing.

Score: 9/10


🤖 Review Details

  • Model: GPT-5.6 Sol
  • Time: 2m 14s
  • Tokens: 58,481 (53,554 in / 4,927 out)
  • Cost: $0.37

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 removed the ultrafix label Aug 30, 2026
@propr-dev
propr-dev Bot merged commit cb90b36 into 1990-epic-synthetic-pool-eus Aug 30, 2026
14 checks passed
@propr-dev
propr-dev Bot deleted the 1990/gpt-5.6-sol-add-persisted-synthetic-a-20260829-2324-okp branch August 30, 2026 01:54
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.

0 participants