Skip to content

Sub-epic: Goal operator experience - #2020

Draft
integry wants to merge 15 commits into
2002-epic-long-running-goalsfrom
2005-epic-goal-operator-experience
Draft

Sub-epic: Goal operator experience#2020
integry wants to merge 15 commits into
2002-epic-long-running-goalsfrom
2005-epic-goal-operator-experience

Conversation

@integry

@integry integry commented Aug 31, 2026

Copy link
Copy Markdown
Owner

Goal operator experience

Tracks #2005 as the UI sub-epic of #2002.

This draft accumulates the dedicated Goals navigation, create/list workflow, and the operator detail experience. It remains draft until its child implementation work and cross-sub-epic contracts are integrated and validated.

Child work

Integration dependencies

Do not merge independently; this sub-epic will be validated as part of the final epic PR for #2002.

propr-dev[bot] and others added 7 commits August 31, 2026 00:24
…GitHub issue #2011:

Summary

New files created:

1. `src/api/goalsApi.ts` — Typed API module with:
   - `GoalListItem`, `GoalsListResponse`, `CreateGoalParams` types
   - `GoalState`, `AutoMergePolicy`, `UltrafixMode` type unions
   - `getGoals()`, `createGoal()`, `getGoal()`, `pauseGoal()`, `resumeGoal()`, `cancelGoal()` API functions following the existing `apiFetch`/`handleApiResponse` pattern

2. `src/pages/GoalsPage.tsx` — Goals list/summary page with:
   - URL-driven filter state (`?state=`, `?search=`, `?page=`) that persists across navigation
   - Debounced search (300ms), state filter dropdown, pagination
   - Empty states (no goals, no search results, no filter results)
   - Demo mode awareness (disables "New Goal" button with explanation)
   - Loading and error states following the PlansPage pattern

3. `src/pages/GoalsPageComponents.tsx` — Supporting components:
   - `GoalStateBadge` — colored state pill for all 7 lifecycle states (active/pausing/paused/recovering/completed/failed/cancelled)
   - `GoalRow` — rich list item showing objective, repository, state, checklist progress bar, agent/model (with requested→effective display when different), active tasks, issue stats, tokens, elapsed/paused time, auto-merge policy, latest event, connection/recovery state, epic PR link
   - `EmptyGoalsState` — three empty state variants
   - `GoalsList` — wraps goal rows in a bordered container
   - `GoalsPagination` — prev/next with page count and total

4. `src/pages/GoalCreatePage.tsx` — Goal creation form with:
   - Objective textarea (10-char minimum validation)
   - Repository selector (populated from instance catalog)
   - Agent + model selector (catalog-filtered, model auto-updates on agent change; explains that model changes apply at safe boundaries)
   - Max concurrent tasks (1–20 numeric input)
   - Auto-merge policy (radio group: disabled/on-approval/all)
   - UltraFix mode (radio group: disabled/enabled/until-goal/max-cycles with inline sub-fields)
   - Client-side validation with field-level errors and accessible labels
   - Submit/cancel, pending state with spinner, demo mode banner + disabled submit
   - Error display for server errors including demo-mode-read-only

Modified files:

5. `src/App.tsx` — Added lazy imports and routes for `/goals` and `/goals/new`
6. `src/components/Layout.tsx` — Added "Goals" entry (with `Target` icon) between Plans and Tasks in the sidebar nav
7. `src/components/MobileBottomNavigation.tsx` — Added Goals to the "More" sheet and to the `getNavigationState` active matching logic
…or commits.

Implemented the PR #2016 follow-up without backend changes or commits.

Key fixes:

- Split `App.tsx` to 287 lines and `GoalCreatePage.tsx` to 56 lines.
- Added all ten contractual goal states; removed `active`.
- Added explicit goal-capability catalog filtering for agents and models.
- Corrected Ultrafix to enabled + independent 1–10 goal and max cycles.
- Added reusable creation idempotency keys, including token-refresh replay.
- Centralized #2006 compatibility normalization in [goalsApi.ts](/tmp/git-processor/worktrees/integry/propr/pr-2016-followup-2026-08-31T00-35-49/propr-ui/src/api/goalsApi.ts).
- Made requested/effective models visually explicit.
- Replaced the nested epic-PR link with valid sibling interactive elements.
- Added focused API, routing, creation, list, socket, accessibility, demo-mode, and mobile-navigation tests.

Verification passed:

- Focused tests: 34/34
- Full UI suite: 492/492
- UI typecheck
- Shared-package typecheck
- ESLint with `--max-warnings=0`
- Production build
- `git diff --check`

PR: #2016
Comment by: @integry (ID: 5472246169)
Model: gpt-5.6-sol
…ting.

Implemented the second owner validation pass without committing.

Key changes:

- Canonical `{ goals, nextCursor }` contract with bounded keyset queries and strict V1 wire decoding in [goalsApi.ts](/home/node/workspace/propr-ui/src/api/goalsApi.ts) and [goalContracts.ts](/home/node/workspace/propr-ui/src/api/goalContracts.ts).
- URL-backed cursor history, back/forward restoration, stale-request suppression, and coalesced goal-specific realtime invalidation in [useGoalsList.ts](/home/node/workspace/propr-ui/src/pages/useGoalsList.ts).
- Removed unkeyed pause/resume/cancel helpers.
- Payload-aware idempotency retry/rotation and conflict recovery.
- Exact creation bounds, including cleared-input validation.
- Required, fail-closed goal capability metadata and allowlist intersection.
- Explicit not-yet-projected statistics instead of fabricated zeros.
- Mobile search, labeled checklist progressbars, demo explanation, and epic/sub-epic/leaf PR creation copy.

Validation results:

- Focused UI tests: `54/54` passed.
- Full UI suite: `70 files, 509 tests` passed.
- `npm run test:unit`: `277/277` passed.
- UI/shared/API typechecks: passed.
- Root/UI/API lint with zero warnings: passed.
- Root/shared/API/UI builds: passed.
- Changed-file workspace lint: passed.
- `git diff --check`: passed.
- `npm run test:full`: `307/308` test runs passed. Only `test/llmMetrics.test.ts` timed out after 180 seconds because Redis was unavailable at `127.0.0.1:6379`; no Redis server, client, or Docker runtime is installed. The suite continued and its final UI run passed all 509 tests.

No commit was created, as instructed. Current HEAD/base SHA: `b4e3c4fc6c24ec8bc9610b5989555168c52cf490`. The automation-generated commit will receive the final resulting SHA.

PR: #2016
Comment by: @integry (ID: 5475318194)
Model: gpt-5.6-sol
Implemented all four follow-up fixes:

- Strict bounded base64url validation for response `nextCursor`, with malformed-cursor regressions.
- Canonical trimmed search handling, whitespace omission, and 200 Unicode-code-point bounds across API, user input, and deep links.
- Strict authoritative `activeSeconds` decoding, typing, and rendering alongside elapsed/paused time.
- Removed the extra blank line at `goalContracts.ts` EOF.

Validation passed:

- Focused UI tests: 39/39
- Full UI test suite
- UI typecheck, zero-warning lint, build
- Shared/API typecheck and builds
- API zero-warning lint
- Root/API unit tests: 277/277
- Root typecheck, lint, build
- `git diff --check`

Current HEAD: `c41a4af18751432380c650373b7685cb099d7e59`.

No commit or push was performed because the harness explicitly requires leaving changes uncommitted for automatic commit handling. PR #2016 was not merged or otherwise modified.

PR: #2016
Comment by: @propr-dev[bot] (ID: 5475938027)
Model: gpt-5.6-sol
Implemented F2–F4 only:

- Removed goal-row navigation to the unavailable `/goals/:goalId` route; epic PR links remain.
- Enforced and displayed a trimmed 10-character objective minimum with boundary tests.
- Bound goals and `nextCursor` to the active query fingerprint, preventing stale rows/pagination after failed or out-of-order requests.
- Left fail-closed capability behavior unchanged.

Validation:

- UI: 70 files, 520 tests passed
- UI typecheck, zero-warning lint, and build passed
- Shared typecheck/build passed
- API typecheck/lint/build passed
- API tests passed; one localhost Web Push test was transiently flaky and passed on isolated retry
- `git diff --check` passed

Per the critical handoff instruction, changes remain uncommitted and unpushed for the system to commit. Therefore, there is no new SHA to report.

PR: #2016
Comment by: @propr-dev[bot] (ID: 5476296543)
Model: gpt-5.6-sol
Implemented F6–F8 only.

- Preserved canonical filtered/search/cursor state through `/goals/new`, including refresh-safe Back, Cancel, and successful-submit returns.
- Added strict same-app `/goals` return-target validation to prevent open redirects.
- Kept filters on empty later pages and exposed usable Previous navigation.
- Enforced the 100-entry cursor-history boundary so all reachable pages remain parseable and reversible.
- Preserved fail-closed catalog behavior; no Web Push changes.

Validation passed:

- Full UI: 70 files, 526 tests
- Focused: 33 tests
- UI typecheck
- UI lint with zero warnings
- UI production build
- `git diff --check`

Per workspace instructions, changes remain uncommitted for the system to commit and push. Current pre-change HEAD is `c39608bd1042`; therefore there is no new follow-up SHA yet. PR was not merged.

PR: #2016
Comment by: @integry (ID: 5476649630)
Model: gpt-5.6-sol
…s-navigation-crea-20260831-0012-ckg

[2011 by Claude Sonnet 4.6] Add Goals navigation, creation workflow, and running summary
Validated by exact-head independent audit, full CI, and GPT-5.6 Sol review (9/10, no blockers).
@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 33409996626, 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
c09af8888d6a: Verifying Checksum
c09af8888d6a: Download complete
4ccd7d76ca98: Verifying Checksum
589002ba0eae: Verifying Checksum
589002ba0eae: Download complete
589002ba0eae: Pull complete
b6b4b7b0e257: Verifying Checksum
b6b4b7b0e257: Download 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 20s

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: 6.261467
      type: 'test'
      ...
    # Subtest: persists a failed state when queue submission fails
    ok 2 - persists a failed state when queue submission fails
      ---
      duration_ms: 1.370857
      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.931764
      type: 'test'
      ...
    # Subtest: times out slow runtime package validation before queueing
    ok 4 - times out slow runtime package validation before queueing
      ---
      duration_ms: 14.435023
      type: 'test'
      ...
    # Subtest: returns package suggestions from configured runtimes
    ok 5 - returns package suggestions from configured runtimes
      ---
      duration_ms: 1.051435
      type: 'test'
      ...
    # Subtest: enforces PROPR_ADMIN_USERS when configured
    ok 6 - enforces PROPR_ADMIN_USERS when configured
      ---
      duration_ms: 0.486229
      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.401733
      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.542875
      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.795342
      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: 4.272426
      type: 'test'
      ...
    # Subtest: resolves the runtime build queue lazily when queueing
    ok 11 - resolves the runtime build queue lazily when queueing
      ---
      duration_ms: 1.011793
      type: 'test'
      ...
    # Subtest: reports apply load failures through the route response
    ok 12 - reports apply load failures through the route response
      ---
      duration_ms: 1.455042
      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.725622
      type: 'test'
      ...
    # Subtest: rejects runtime verification without runtime-management permission
    ok 14 - rejects runtime verification without runtime-management permission
      ---
      duration_ms: 0.552612
      type: 'test'
      ...
    1..14
ok 1 - agent runtime package routes
  ---
  duration_ms: 40.246467
  type: 'suite'
  ...
# [2026-08-31 15:43:19.044 +0000] �[32mINFO�[39m: �[36mSQLite database connection established successfully�[39m
#     filename: "/home/runner/work/propr/propr/data/propr.test.sqlite"
#     environment: "test"
# [2026-08-31 15:43:19.081 +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: 12.410015
  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: 8.368476
  type: 'test'
  ...
# Subtest: temporary cleanup refuses paths outside its configured root
ok 4 - temporary cleanup refuses paths outside its configured root
  ---
  duration_ms: 1.057758
  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.77807
  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: 3.697703
  type: 'test'
  ...
# [2026-08-31 15:43:18.543 +0000] �[32mINFO�[39m: �[36mCreated data directory for SQLite database�[39m
#     directory: "/home/runner/work/propr/propr/data"
# [2026-08-31 15:43:19.044 +0000] �[32mINFO�[39m: �[36mSQLite database connection established successfully�[39m
#     filename: "/home/runner/work/propr/propr/data/propr.test.sqlite"
#     environment: "test"
# [2026-08-31 15:43:19.069 +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: 11.833997
  type: 'test'
  ...
# Subtest: API config subscription reloads repository updates
ok 8 - API config subscription reloads repository updates
  ---
  duration_ms: 0.516426
  type: 'test'
  ...
# Subtest: API config subscription ignores unrelated and malformed events
ok 9 - API config subscription ignores unrelated and malformed events
  ---
  duration_ms: 0.454432
  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.579564
  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: 2.030679
  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: 1.797097
  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.481541
  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.584472
  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.262777
  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.207464
  type: 'test'
  ...
# Subtest: explicit custom GitHub web auth remains supported
ok 17 - explicit custom GitHub web auth remains supported
  ---
  duration_ms: 0.272545
  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.265191
  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.780535
  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: 46.546808
  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.054822
  type: 'test'
  ...
# Subtest: accepts Docker IDs and ProPR-generated container names
ok 22 - accepts Docker IDs and ProPR-generated container names
  ---
  duration_ms: 1.235618
  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.218074
  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: 215.809232
  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: 13.09408
  type: 'test'
  ...
# Subtest: instance authorization
    # Subtest: defaults unassigned authenticated users to members on a new installation
    ok 1 - defaults unassigned authenticated users to members on a new installation
      ---
      duration_ms: 16.931002
      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.387951
      type: 'test'
      ...
    # Subtest: grants full admin permissions to PROPR_ADMIN_USERS
    ok 3 - grants full admin permissions to PROPR_ADMIN_USERS
      ---
      duration_ms: 4.422504
      type: 'test'
      ...
    # Subtest: resolves durable roles by numeric GitHub ID after a username change
    ok 4 - resolves durable roles by numeric GitHub ID after a username change
      ---
      duration_ms: 4.191747
      type: 'test'
      ...
    # Subtest: resolves demo users without installation permissions
    ok 5 - resolves demo users without installation permissions
      ---
      duration_ms: 3.018175
      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: 5.311269
      type: 'test'
      ...
    # Subtest: current-user responses expose capabilities without OAuth credentials
    ok 7 - current-user responses expose capabilities without OAuth credentials
      ---
      duration_ms: 4.885501
      type: 'test'
      ...
    1..7
ok 26 - instance authorization
  ---
  duration_ms: 46.999424
  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.671907
      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: 21.930661
      type: 'test'
      ...
    # Subtest: adds explicit members and writes an audit entry
    ok 3 - adds explicit members and writes an audit entry
      ---
      duration_ms: 5.131438
      type: 'test'
      ...
    # Subtest: keeps environment authority separate from durable role assignments
    ok 4 - keeps environment authority separate from durable role assignments
      ---
      duration_ms: 3.207446
      type: 'test'
      ...
    # Subtest: prevents removing the last durable administrator
    ok 5 - prevents removing the last durable administrator
      ---
      duration_ms: 9.313224
      type: 'test'
      ...
    # Subtest: allows role changes once another administrator exists
    ok 6 - allows role changes once another administrator exists
      ---
      duration_ms: 7.82498
      type: 'test'
      ...
    1..6
ok 27 - instance member service
  ---
  duration_ms: 55.6598
  type: 'suite'
  ...
# Subtest: instance catalog
    # Subtest: returns operational agent and repository fields without installation internals
    ok 1 - returns operational agent and repository fields without installation internals
      ---
      duration_ms: 3.785756
      type: 'test'
      ...
    # Subtest: projects indexing status only for enabled repository and branch entries
    ok 2 - projects indexing status only for enabled repository and branch entries
      ---
      duration_ms: 2.789251
      type: 'test'
      ...
    1..2
ok 28 - instance catalog
  ---
  duration_ms: 6.72282
  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: 15.904481
      type: 'test'
      ...
    # Subtest: rejects a non-numeric audit limit with a 400
    ok 2 - rejects a non-numeric audit limit with a 400
      ---
      duration_ms: 2.481763
      type: 'test'
      ...
    # Subtest: rejects GitHub usernames with trailing or consecutive hyphens
    ok 3 - rejects GitHub usernames with trailing or consecutive hyphens
      ---
      duration_ms: 2.445586
      type: 'test'
      ...
    1..3
ok 29 - instance admin routes
  ---
  duration_ms: 21.027161
  type: 'suite'
  ...
# [2026-08-31 15:43:21.615 +0000] �[32mINFO�[39m: �[36mSQLite database connection established successfully�[39m
#     filename: "/home/runner/work/propr/propr/data/propr.test.sqlite"
#     environment: "test"
# [2026-08-31 15:43:21.737 +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: 1.052838
  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.164664
  type: 'test'
  ...
# Subtest: an explicit API listen host overrides environment defaults
ok 32 - an explicit API listen host overrides environment defaults
  ---
  duration_ms: 0.223664
  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: 4.318972
  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.942505
  type: 'test'
  ...
# [2026-08-31 15:43:23.674 +0000] �[32mINFO�[39m: �[36mSQLite database connection established successfully�[39m
#     filename: "/home/runner/work/propr/propr/data/propr.test.sqlite"
#     environment: "test"
# [2026-08-31 15:43:23.676 +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.704888
  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: 17.06575
  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: 36.121293
  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: 27.196499
  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: 18.352512
  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: 26.156643
  type: 'test'
  ...
# Subtest: resolves secure defaults and explicit positive-integer overrides
ok 41 - resolves secure defaults and explicit positive-integer overrides
  ---
  duration_ms: 0.536032
  type: 'test'
  ...
# Subtest: rejects invalid overrides instead of silently disabling protection
ok 42 - rejects invalid overrides instead of silently disabling protection
  ---
  duration_ms: 0.789781
  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: 53.200329
      type: 'test'
      ...
    # Subtest: members can read only the sanitized catalog endpoints
    ok 2 - members can read only the sanitized catalog endpoints
      ---
      duration_ms: 58.200491
      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: 22.974685
      type: 'test'
      ...
    1..3
ok 43 - assembled instance permission routes
  ---
  duration_ms: 136.396154
  type: 'suite'
  ...
# [2026-08-31 15:43:24.159 +0000] �[32mINFO�[39m: �[36mSQLite database connection established successfully�[39m
#     filename: "/home/runner/work/propr/propr/data/propr.test.sqlite"
#     environment: "test"
# [2026-08-31 15:43:24.290 +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'
# }
# 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: 2251.241019
  type: 'test'
  ...
# Error loading summarization warnings: SqliteError: select * from `system_configs` where `key` = 'summarization_runtime_state' limit 1 - no such table: system_configs
#     at Database.prepare (/home/runner/work/propr/propr/node_modules/better-sqlite3/lib/methods/wrappers.js:5:21)
#     at Client_BetterSQLite3._query (/home/runner/work/propr/propr/node_modules/knex/lib/dialects/better-sqlite3/index.js:35:34)
#     at executeQuery (/home/runner/work/propr/propr/node_modules/knex/lib/execution/internal/query-executioner.js:37:17)
#     at Client_BetterSQLite3.query (/home/runner/work/propr/propr/node_modules/knex/lib/client.js:154:12)
#     at Runner.query (/home/runner/work/propr/propr/node_modules/knex/lib/execution/runner.js:141:36)
#     at ensureConnectionCallback (/home/runner/work/propr/propr/node_modules/knex/lib/execution/internal/ensure-connection-callback.js:13:17)
#     at Runner.ensureConnection (/home/runner/work/propr/propr/node_modules/knex/lib/execution/runner.js:318:20)
#     at async Runner.run (/home/runner/work/propr/propr/node_modules/knex/lib/execution/runner.js:30:19)
#     at async getConfigWithClient (/home/runner/work/propr/propr/packages/core/src/config/configStore.ts:17:24)
#     at async loadSummarizationRuntimeState (/home/runner/work/propr/propr/packages/core/src/config/configManagerSummarization.ts:101:19) {
#   code: 'SQLITE_ERROR'
# }
# [notifications] Failed to project system health snapshot: Error: notification persistence unavailable
#     at projectSystemSnapshot (/home/runner/work/propr/propr/packages/api/test/statusRoutes.test.ts: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: 3.457438
  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.595743
  type: 'test'
  ...
# Subtest: /api/status isolates system notification projection failures
ok 47 - /api/status isolates system notification projection failures
  ---
  duration_ms: 7.254654
  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 surfaces unified agent image outages
ok 48 - /api/status surfaces unified agent image outages
  ---
  duration_ms: 7.230621
  type: 'test'
  ...
# Subtest: /api/status includes warnings field in demo mode
ok 49 - /api/status includes warnings field in demo mode
  ---
  duration_ms: 1.173752
  type: 'test'
  ...
# Subtest: /api/status caches agent health checks briefly
ok 50 - /api/status caches agent health checks briefly
  ---
  duration_ms: 4.454102
  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: 5.406585
  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 reports connected intake status when routing state is live
ok 52 - /api/status reports connected intake status when routing state is live
  ---
  duration_ms: 6.426172
  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: 3.501881
  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: 9.832124
  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'
# }
# [2026-08-31 15:43:25.086 +0000] �[32mINFO�[39m: �[36mSQLite database connection established successfully�[39m
#     filename: "/home/runner/work/propr/propr/data/propr.test.sqlite"
#     environment: "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: 3.369585
  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: 4.781428
  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'
# }
# [2026-08-31 15:43:25.089 +0000] �[31mERROR�[39m: �[36mFailed to load config from DB�[39m
#     key: "summarization_runtime_state"
#     error: "select * from `system_configs` where `key` = 'summarization_runtime_state' limit 1 - no such table: system_configs"
# [2026-08-31 15:43:25.103 +0000] �[31mERROR�[39m: �[36mFailed to load config from DB�[39m
#     key: "summarization_runtime_state"
#     error: "select * from `system_configs` where `key` = 'summarization_runtime_state' limit 1 - no such table: system_configs"
# [2026-08-31 15:43:25.105 +0000] �[31mERROR�[39m: �[36mFailed to load config from DB�[39m
#     key: "summarization_runtime_state"
#     error: "select * from `system_configs` where `key` = 'summarization_runtime_state' limit 1 - no such table: system_configs"
# [2026-08-31 15:43:25.122 +0000] �[31mERROR�[39m: �[36mFailed to load config from DB�[39m
#     key: "summarization_runtime_state"
#     error: "select * from `system_configs` where `key` = 'summarization_runtime_state' limit 1 - no such table: system_configs"
# [2026-08-31 15:43:25.124 +0000] �[31mERROR�[39m: �[36mFailed to load config from DB�[39m
#     key: "summarization_runtime_state"
#     error: "select * from `system_configs` where `key` = 'summarization_runtime_state' limit 1 - no such table: system_configs"
# [2026-08-31 15:43:25.129 +0000] �[31mERROR�[39m: �[36mFailed to load config from DB�[39m
#     key: "summarization_runtime_state"
#     error: "select * from `system_configs` where `key` = 'summarization_runtime_state' limit 1 - no such table: system_configs"
# 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'
# }
# [2026-08-31 15:43:25.136 +0000] �[31mERROR�[39m: �[36mFailed to load config from DB�[39m
#     key: "summarization_runtime_state"
#     error: "select * from `system_configs` where `key` = 'summarization_runtime_state' limit 1 - no such table: system_configs"
# [2026-08-31 15:43:25.138 +0000] �[31mERROR�[39m: �[36mFailed to load config from DB�[39m
#     key: "summarization_runtime_state"
#     error: "select * from `system_configs` where `key` = 'summarization_runtime_state' limit 1 - no such table: system_configs"
# [2026-08-31 15:43:25.145 +0000] �[31mERROR�[39m: �[36mFailed to load config from DB�[39m
#     key: "summarization_runtime_state"
#     error: "select * from `system_configs` where `key` = 'summarization_runtime_state' limit 1 - no such table: system_configs"
# [2026-08-31 15:43:25.153 +0000] �[31mERROR�[39m: �[36mFailed to load config from DB�[39m
#     key: "summarization_runtime_state"
#     error: "select * from `system_configs` where `key` = 'summarization_runtime_state' limit 1 - no such table: system_configs"
# [2026-08-31 15:43:25.158 +0000] �[31mERROR�[39m: �[36mFailed to load config from DB�[39m
#     key: "summarization_runtime_state"
#     error: "select * from `system_configs` where `key` = 'summarization_runtime_state' limit 1 - no such table: system_configs"
# 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: 5.417766
  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 loadSumma

...(truncated)

View Workflow

@integry

integry commented Aug 31, 2026

Copy link
Copy Markdown
Owner Author

Owner integrated audit at exact head 4b910a8 found six combined-branch blockers. Keep this draft sub-epic PR unmerged and implement all six with Codex GPT-5.6 Sol only. Preserve every previously audited list/create/detail/operator behavior, replay fencing, content sanitization, authorization, accessibility, and test invariant.

  1. Identity-bind list state and socket authentication. useGoalsList currently fingerprints URL state only; an account/role/authorization change can leave the prior owner's rows visible, a stale prior-owner response can commit, and same-query 403/404 keeps old rows/cursors. Make authorization identity part of ownership/fencing, synchronously clear prior rows/cursors on identity change and on 403/404, and reject stale completions. Key/recreate SocketProvider authentication on identity change, disconnecting the old socket and removing old subscriptions/listeners before a newly authenticated connection. Tests must cover A->B with A response in flight, stale A completion, 403/404 clearing, and socket disconnect/resubscribe.

  2. Bound retained goal-event history and update complexity, not only rendered DOM. Current merge rebuilds/sorts all events for every replay/live/older-page update, yielding unbounded memory and cumulative O(n^2 log n). Introduce an explicit finite retention policy/data structure across live events, replay pages, and older-history loading while preserving authoritative latest sequence, dedupe, gap recovery, cursor continuation, follow-tail, and scroll anchoring. Tests must feed far beyond the bound across all ingestion paths, cover eviction plus duplicate/gap recovery, and interleave live events with an older-page load without losing tail or anchor state.

  3. Make list invalidation starvation-free. The 100ms throttle currently starts a new refresh that aborts the prior request under sustained >10Hz summary traffic; off-page/unknown goal IDs can perpetuate this. Allow at most one refresh in flight plus one bounded coalesced trailing refresh, do not repeatedly abort the in-flight refresh, and guarantee the newest result commits. Add a deferred-request test with sustained visible and off-page events across multiple timer intervals proving one in-flight request, one trailing refresh, no abort storm, and eventual latest commit.

  4. Apply the existing absolute-HTTPS external-link policy to list epicPrUrl. Reject javascript:, data:, protocol-relative, malformed, and HTTP values. Render only absolute HTTPS with target="_blank" and rel="noopener noreferrer"; test every case.

  5. Make terminal clipboard output safe and failure-aware. Sanitize untrusted source and turnId metadata as well as content so labels are single-line and free of ANSI/C0/control/newline injection. Handle missing or rejected Clipboard API without unhandled promises and expose an accessible polite failure status while retaining the success status. Test malicious metadata, unavailable clipboard, rejected clipboard, success, and inert copied content.

  6. Remove nested main landmarks. The shared Layout already owns the application <main>; goal list/create/detail routes must not nest another one. Add composed-route tests through the real Layout for /goals, /goals/new, and /goals/:goalId, asserting exactly one main landmark each.

Run focused adversarial tests, the complete UI tests, typecheck/lint/build, full repository suite where available, and git diff --check. Keep the correction narrowly scoped to these integration findings and publish one new exact head for re-audit.

@propr-dev

propr-dev Bot commented Aug 31, 2026

Copy link
Copy Markdown
Contributor

Applied the requested follow-up changes in commit 72bb909

Summary of Changes

Implemented all six audit blockers and left changes uncommitted as required.

Key outcomes:

  • Authorization-bound goal list state and socket reauthentication with stale-response fencing.
  • Starvation-free list invalidation with one in-flight and one coalesced trailing refresh.
  • Goal-event retention capped at 1,000 across live, replay, and older-history ingestion.
  • Strict absolute-HTTPS list epic links.
  • Sanitized clipboard metadata/content with accessible success and failure states.
  • Exactly one <main> landmark across all goal routes through the real Layout.

Validation passed:

  • Focused adversarial tests: 86 tests.
  • Complete UI suite: 74 files, 618 tests.
  • UI and repository typecheck, lint, and build.
  • git diff --check.

The full repository suite completed through test file 159/308, then stalled at file 160 because Redis was unavailable (ECONNREFUSED 127.0.0.1:6379); the hung runner was stopped. No commit or PR operation was performed.


🤖 Implementation Details

  • Model: GPT-5.6 Sol (high)
  • Requested By: @integry
  • Time: 29m 59s
  • Tokens: 10,433,012 (10,400,320 in / 32,692 out)
  • Cost: $7.08
  • Subscription usage: Weekly +2%

Undo ChangesView Task Execution


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

🤖 Generated by ProPR
Processing comment ID: 5481209200✓

@integry integry removed the AI label Aug 31, 2026
…tted as required.

Implemented all six audit blockers and left changes uncommitted as required.

Key outcomes:

- Authorization-bound goal list state and socket reauthentication with stale-response fencing.
- Starvation-free list invalidation with one in-flight and one coalesced trailing refresh.
- Goal-event retention capped at 1,000 across live, replay, and older-history ingestion.
- Strict absolute-HTTPS list epic links.
- Sanitized clipboard metadata/content with accessible success and failure states.
- Exactly one `<main>` landmark across all goal routes through the real Layout.

Validation passed:

- Focused adversarial tests: 86 tests.
- Complete UI suite: 74 files, 618 tests.
- UI and repository typecheck, lint, and build.
- `git diff --check`.

The full repository suite completed through test file 159/308, then stalled at file 160 because Redis was unavailable (`ECONNREFUSED 127.0.0.1:6379`); the hung runner was stopped. No commit or PR operation was performed.

PR: #2020
Comment by: @integry (ID: 5481209200)
Model: gpt-5.6-sol
@integry integry added AI and removed AI labels Aug 31, 2026
@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 33416447788, 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
589002ba0eae: Verifying Checksum
589002ba0eae: Download complete
c09af8888d6a: Verifying Checksum
c09af8888d6a: Download complete
4ccd7d76ca98: Verifying Checksum
4ccd7d76ca98: 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 19s

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: 5.707485
      type: 'test'
      ...
    # Subtest: persists a failed state when queue submission fails
    ok 2 - persists a failed state when queue submission fails
      ---
      duration_ms: 1.54778
      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.079494
      type: 'test'
      ...
    # Subtest: times out slow runtime package validation before queueing
    ok 4 - times out slow runtime package validation before queueing
      ---
      duration_ms: 14.188708
      type: 'test'
      ...
    # Subtest: returns package suggestions from configured runtimes
    ok 5 - returns package suggestions from configured runtimes
      ---
      duration_ms: 0.935452
      type: 'test'
      ...
    # Subtest: enforces PROPR_ADMIN_USERS when configured
    ok 6 - enforces PROPR_ADMIN_USERS when configured
      ---
      duration_ms: 0.453668
      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.348856
      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: 2.311264
      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.662226
      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: 12.377026
      type: 'test'
      ...
    # Subtest: resolves the runtime build queue lazily when queueing
    ok 11 - resolves the runtime build queue lazily when queueing
      ---
      duration_ms: 0.925315
      type: 'test'
      ...
    # Subtest: reports apply load failures through the route response
    ok 12 - reports apply load failures through the route response
      ---
      duration_ms: 0.40034
      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.576675
      type: 'test'
      ...
    # Subtest: rejects runtime verification without runtime-management permission
    ok 14 - rejects runtime verification without runtime-management permission
      ---
      duration_ms: 0.340566
      type: 'test'
      ...
    1..14
ok 1 - agent runtime package routes
  ---
  duration_ms: 44.253319
  type: 'suite'
  ...
# [2026-08-31 16:52:53.371 +0000] �[32mINFO�[39m: �[36mCreated data directory for SQLite database�[39m
#     directory: "/home/runner/work/propr/propr/data"
# [2026-08-31 16:52:54.844 +0000] �[32mINFO�[39m: �[36mSQLite database connection established successfully�[39m
#     filename: "/home/runner/work/propr/propr/data/propr.test.sqlite"
#     environment: "test"
# [2026-08-31 16:52:54.884 +0000] �[32mINFO�[39m: �[36mSQLite database connection closed�[39m
# Subtest: upload handler removes Multer files rejected before attachment processing
ok 2 - upload handler removes Multer files rejected before attachment processing
  ---
  duration_ms: 10.7716
  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: 4.890972
  type: 'test'
  ...
# Subtest: temporary cleanup refuses paths outside its configured root
ok 4 - temporary cleanup refuses paths outside its configured root
  ---
  duration_ms: 0.658567
  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.448784
  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: 3.120511
  type: 'test'
  ...
# [2026-08-31 16:52:54.890 +0000] �[32mINFO�[39m: �[36mSQLite database connection established successfully�[39m
#     filename: "/home/runner/work/propr/propr/data/propr.test.sqlite"
#     environment: "test"
# [2026-08-31 16:52:54.909 +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: 5.741905
  type: 'test'
  ...
# Subtest: API config subscription reloads repository updates
ok 8 - API config subscription reloads repository updates
  ---
  duration_ms: 0.521904
  type: 'test'
  ...
# Subtest: API config subscription ignores unrelated and malformed events
ok 9 - API config subscription ignores unrelated and malformed events
  ---
  duration_ms: 0.384967
  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.518539
  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.573573
  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.325468
  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: 3.38839
  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.800423
  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.224096
  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.199512
  type: 'test'
  ...
# Subtest: explicit custom GitHub web auth remains supported
ok 17 - explicit custom GitHub web auth remains supported
  ---
  duration_ms: 3.189778
  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.303737
  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.728932
  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: 91.650382
  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: 0.957046
  type: 'test'
  ...
# Subtest: accepts Docker IDs and ProPR-generated container names
ok 22 - accepts Docker IDs and ProPR-generated container names
  ---
  duration_ms: 1.168825
  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.239518
  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: 145.983207
  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: 8.240781
  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: 10.585979
      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.656057
      type: 'test'
      ...
    # Subtest: grants full admin permissions to PROPR_ADMIN_USERS
    ok 3 - grants full admin permissions to PROPR_ADMIN_USERS
      ---
      duration_ms: 2.490205
      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.572216
      type: 'test'
      ...
    # Subtest: resolves demo users without installation permissions
    ok 5 - resolves demo users without installation permissions
      ---
      duration_ms: 2.066825
      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.021784
      type: 'test'
      ...
    # Subtest: current-user responses expose capabilities without OAuth credentials
    ok 7 - current-user responses expose capabilities without OAuth credentials
      ---
      duration_ms: 3.425744
      type: 'test'
      ...
    1..7
ok 26 - instance authorization
  ---
  duration_ms: 29.077623
  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.442317
      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.886397
      type: 'test'
      ...
    # Subtest: adds explicit members and writes an audit entry
    ok 3 - adds explicit members and writes an audit entry
      ---
      duration_ms: 7.46419
      type: 'test'
      ...
    # Subtest: keeps environment authority separate from durable role assignments
    ok 4 - keeps environment authority separate from durable role assignments
      ---
      duration_ms: 3.775117
      type: 'test'
      ...
    # Subtest: prevents removing the last durable administrator
    ok 5 - prevents removing the last durable administrator
      ---
      duration_ms: 5.23105
      type: 'test'
      ...
    # Subtest: allows role changes once another administrator exists
    ok 6 - allows role changes once another administrator exists
      ---
      duration_ms: 7.105725
      type: 'test'
      ...
    1..6
ok 27 - instance member service
  ---
  duration_ms: 33.421689
  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.59038
      type: 'test'
      ...
    # Subtest: projects indexing status only for enabled repository and branch entries
    ok 2 - projects indexing status only for enabled repository and branch entries
      ---
      duration_ms: 2.259773
      type: 'test'
      ...
    1..2
ok 28 - instance catalog
  ---
  duration_ms: 4.980356
  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: 3.764551
      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.978259
      type: 'test'
      ...
    # Subtest: rejects GitHub usernames with trailing or consecutive hyphens
    ok 3 - rejects GitHub usernames with trailing or consecutive hyphens
      ---
      duration_ms: 2.257563
      type: 'test'
      ...
    1..3
ok 29 - instance admin routes
  ---
  duration_ms: 8.213582
  type: 'suite'
  ...
# [2026-08-31 16:52:56.055 +0000] �[32mINFO�[39m: �[36mSQLite database connection established successfully�[39m
#     filename: "/home/runner/work/propr/propr/data/propr.test.sqlite"
#     environment: "test"
# [2026-08-31 16:52:56.126 +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: 1.102973
  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.221795
  type: 'test'
  ...
# Subtest: an explicit API listen host overrides environment defaults
ok 32 - an explicit API listen host overrides environment defaults
  ---
  duration_ms: 0.144329
  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.639626
  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.665355
  type: 'test'
  ...
# [2026-08-31 16:52:59.903 +0000] �[32mINFO�[39m: �[36mSQLite database connection established successfully�[39m
#     filename: "/home/runner/work/propr/propr/data/propr.test.sqlite"
#     environment: "test"
# [2026-08-31 16:52:59.904 +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: 171.012064
  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: 24.384133
  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: 31.065993
  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: 30.614842
  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: 11.564701
  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: 15.402641
  type: 'test'
  ...
# Subtest: resolves secure defaults and explicit positive-integer overrides
ok 41 - resolves secure defaults and explicit positive-integer overrides
  ---
  duration_ms: 0.374248
  type: 'test'
  ...
# Subtest: rejects invalid overrides instead of silently disabling protection
ok 42 - rejects invalid overrides instead of silently disabling protection
  ---
  duration_ms: 0.707596
  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: 42.171773
      type: 'test'
      ...
# [2026-08-31 16:52:59.581 +0000] �[32mINFO�[39m: �[36mSQLite database connection established successfully�[39m
#     filename: "/home/runner/work/propr/propr/data/propr.test.sqlite"
#     environment: "test"
    # Subtest: members can read only the sanitized catalog endpoints
    ok 2 - members can read only the sanitized catalog endpoints
      ---
      duration_ms: 97.738494
      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: 47.618838
      type: 'test'
      ...
    1..3
ok 43 - assembled instance permission routes
  ---
  duration_ms: 189.083546
  type: 'suite'
  ...
# [2026-08-31 16:52:59.766 +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: 2303.423675
  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.262058
  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: 4.460921
  type: 'test'
  ...
# Subtest: /api/status isolates system notification projection failures
ok 47 - /api/status isolates system notification projection failures
  ---
  duration_ms: 6.130682
  type: 'test'
  ...
# Subtest: /api/status surfaces unified agent image outages
ok 48 - /api/status surfaces unified agent image outages
  ---
  duration_ms: 2.248811
  type: 'test'
  ...
# Subtest: /api/status includes warnings field in demo mode
ok 49 - /api/status includes warnings field in demo mode
  ---
  duration_ms: 1.214204
  type: 'test'
  ...
# Error loading summarization warnings: SqliteError: select * from `system_configs` where `key` = 'summarization_runtime_state' limit 1 - no such table: system_configs
#     at Database.prepare (/home/runner/work/propr/propr/node_modules/better-sqlite3/lib/methods/wrappers.js:5:21)
#     at Client_BetterSQLite3._query (/home/runner/work/propr/propr/node_modules/knex/lib/dialects/better-sqlite3/index.js:35:34)
#     at executeQuery (/home/runner/work/propr/propr/node_modules/knex/lib/execution/internal/query-executioner.js:37:17)
#     at Client_BetterSQLite3.query (/home/runner/work/propr/propr/node_modules/knex/lib/client.js:154:12)
#     at Runner.query (/home/runner/work/propr/propr/node_modules/knex/lib/execution/runner.js:141:36)
#     at ensureConnectionCallback (/home/runner/work/propr/propr/node_modules/knex/lib/execution/internal/ensure-connection-callback.js:13:17)
#     at Runner.ensureConnection (/home/runner/work/propr/propr/node_modules/knex/lib/execution/runner.js:318:20)
#     at async Runner.run (/home/runner/work/propr/propr/node_modules/knex/lib/execution/runner.js:30:19)
#     at async getConfigWithClient (/home/runner/work/propr/propr/packages/core/src/config/configStore.ts:17:24)
#     at async loadSummarizationRuntimeState (/home/runner/work/propr/propr/packages/core/src/config/configManagerSummarization.ts:101:19) {
#   code: 'SQLITE_ERROR'
# }
# Error loading summarization warnings: SqliteError: select * from `system_configs` where `key` = 'summarization_runtime_state' limit 1 - no such table: system_configs
#     at Database.prepare (/home/runner/work/propr/propr/node_modules/better-sqlite3/lib/methods/wrappers.js:5:21)
#     at Client_BetterSQLite3._query (/home/runner/work/propr/propr/node_modules/knex/lib/dialects/better-sqlite3/index.js:35:34)
#     at executeQuery (/home/runner/work/propr/propr/node_modules/knex/lib/execution/internal/query-executioner.js:37:17)
#     at Client_BetterSQLite3.query (/home/runner/work/propr/propr/node_modules/knex/lib/client.js:154:12)
#     at Runner.query (/home/runner/work/propr/propr/node_modules/knex/lib/execution/runner.js:141:36)
#     at ensureConnectionCallback (/home/runner/work/propr/propr/node_modules/knex/lib/execution/internal/ensure-connection-callback.js:13:17)
#     at Runner.ensureConnection (/home/runner/work/propr/propr/node_modules/knex/lib/execution/runner.js:318:20)
#     at async Runner.run (/home/runner/work/propr/propr/node_modules/knex/lib/execution/runner.js:30:19)
#     at async getConfigWithClient (/home/runner/work/propr/propr/packages/core/src/config/configStore.ts:17:24)
#     at async loadSummarizationRuntimeState (/home/runner/work/propr/propr/packages/core/src/config/configManagerSummarization.ts:101:19) {
#   code: 'SQLITE_ERROR'
# }
# Subtest: /api/status caches agent health checks briefly
ok 50 - /api/status caches agent health checks briefly
  ---
  duration_ms: 8.013183
  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 resolved auth mode and event intake mode
ok 51 - /api/status reports resolved auth mode and event intake mode
  ---
  duration_ms: 2.2622
  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: 2.084767
  type: 'test'
  ...
# Error loading summarization warnings: SqliteError: select * from `system_configs` where `key` = 'summarization_runtime_state' limit 1 - no such table: system_configs
#     at Database.prepare (/home/runner/work/propr/propr/node_modules/better-sqlite3/lib/methods/wrappers.js:5:21)
#     at Client_BetterSQLite3._query (/home/runner/work/propr/propr/node_modules/knex/lib/dialects/better-sqlite3/index.js:35:34)
#     at executeQuery (/home/runner/work/propr/propr/node_modules/knex/lib/execution/internal/query-executioner.js:37:17)
#     at Client_BetterSQLite3.query (/home/runner/work/propr/propr/node_modules/knex/lib/client.js:154:12)
#     at Runner.query (/home/runner/work/propr/propr/node_modules/knex/lib/execution/runner.js:141:36)
#     at ensureConnectionCallback (/home/runner/work/propr/propr/node_modules/knex/lib/execution/internal/ensure-connection-callback.js:13:17)
#     at Runner.ensureConnection (/home/runner/work/propr/propr/node_modules/knex/lib/execution/runner.js:318:20)
#     at async Runner.run (/home/runner/work/propr/propr/node_modules/knex/lib/execution/runner.js:30:19)
#     at async getConfigWithClient (/home/runner/work/propr/propr/packages/core/src/config/configStore.ts:17:24)
#     at async loadSummarizationRuntimeState (/home/runner/work/propr/propr/packages/core/src/config/configManagerSummarization.ts:101:19) {
#   code: 'SQLITE_ERROR'
# }
# Subtest: /api/status reports disconnected intake status when routing state is down
ok 53 - /api/status reports disconnected intake status when routing state is down
  ---
  duration_ms: 2.382914
  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 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.958437
  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: 1.591006
  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 includes routing state published by the daemon
ok 56 - /api/status includes routing state published by the daemon
  ---
  duration_ms: 1.542421
  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 exposes only validated UI-safe Connect account fields
ok 57 - /api/status exposes only validated UI-safe Connect account fields
  ---
  duration_ms: 10.196898
  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: 6.680276
  type: 'test'
  ...
# Error loading summarization warnings: SqliteError: select * from `system_configs` where `key` = 'summarization_runtime_state' limit 1 - no such table: system_configs
#     at Database.prepare (/home/runner/work/propr/propr/node_modules/better-sqlite3/lib/methods/wrappers.js:5:21)
#     at Client_BetterSQLite3._query (/home/runner/work/propr/propr/node_modules/knex/lib/dialects/better-sqlite3/index.js:35:34)
#     at executeQuery (/home/runner/work/propr/propr/node_modules/knex/lib/execution/internal/query-executioner.js:37:17)
#     at Client_BetterSQLite3.query (/home/runner/work/propr/propr/node_modules/knex/lib/client.js:154:12)
#     at Runner.query (/home/runner/work/propr/propr/node_modules/knex/lib/execution/runner.js:141:36)
#     at ensureConnectionCallback (/home/runner/work/propr/propr/node_modules/knex/lib/execution/internal/ensure-connection-callback.js:13:17)
#     at Runner.ensureConnection (/home/runner/work/propr/propr/node_modules/knex/lib/execution/runner.js:318:20)
#     at async Runner.run (/home/runner/work/propr/propr/node_modules/knex/lib/execution/runner.js:30:19)
#     at async getConfigWithClient (/home/runner/work/propr/propr/packages/core/src/config/configStore.ts:17:24)
#     at async loadSummarizationRuntimeState (/home/runner/work/propr/propr/packages/core/src/config/configManagerSummarization.ts:101:19) {
#   code: 'SQLITE_ERROR'
# }
# Error loading summarization warnings: SqliteError: select * from `system_configs` where `key` = 'summarization_runtime_state' limit 1 - no such table: system_configs
#     at Database.prepare (/home/runner/work/propr/propr/node_modules/better-sqlite3/lib/methods/wrappers.js:5:21)
#     at Client_BetterSQLite3._query (/home/runner/work/propr/propr/node_modules/knex/lib/dialects/better-sqlite3/index.js:35:34)
#     at executeQuery (/home/runner/work/propr/propr/node_modules/knex/lib/execution/internal/query-executioner.js:37:17)
#     at Client_BetterSQLite3.query (/home/runner/work/propr/propr/node_modules/knex/lib/client.js:154:12)
#     at Runner.query (/home/runner/work/propr/propr/node_modules/knex/lib/execution/runner.js:141:36)
#     at ensureConnectionCallback (/home/runner/work/propr/propr/node_modules/knex/lib/execution/internal/ensure-connection-callback.js:13:17)
#     at Runner.ensureConnection (/home/runner/work/propr/propr/node_modules/knex/lib/execution/runner.js:318:20)
#     at async Runner.run (/home/runner/work/propr/propr/node_modules/knex/lib/execution/runner.js:30:19)
#     at async getConfigWithClient (/home/runner/work/propr/propr/packages/core/src/config/configStore.ts:17:24)
#     at async loadSummarizationRuntimeState (/home/runner/work/propr/propr/packages/core/src/config/configManagerSummarization.ts:101:19) {
#   code: 'SQLITE_ERROR'
# }
# Subtest: /api/status drops malformed or disconnected Connect account state without assuming Community
ok 59 - /api/status drops malformed or disconnected Connect account state without assuming Community
  ---
  duration_ms: 5.039838
  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 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: 2.416341
  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.478197
  type: 'test'
  ...
# 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: 1.341126
  type: 'test'
  ...
# Subtest: /api/status omits malformed routing state
ok 63 - /api/status omits malformed routing state
  ---
  duration_ms: 1.493315
  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 omits routing state with a malformed lastAckAt timestamp
ok 64 - /api/status omits routing state with a malformed lastAckAt timestamp
  ---
  duration_ms: 2.848151
  type: 'test'
  ...
# Subtest: /api/status omits routing state when none is published
ok 65 - /api/status omits routing state when none is published
  ---
  duration_ms: 1.508919
  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: 1.533009
  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)
#    

...(truncated)

View Workflow

@integry integry added the AI label Aug 31, 2026
@integry

integry commented Aug 31, 2026

Copy link
Copy Markdown
Owner Author

Owner exact-head re-audit at 72bb909 found two remaining blockers. Keep the draft sub-epic PR unmerged and fix both with Codex GPT-5.6 Sol only, preserving all six now-verified functional corrections and their tests.

  1. Preserve the visible historical viewport anchor across bounded retention eviction. The current fixed oldest-500/newest-500 trim has no viewport knowledge; repeated 200-event older-page loads can evict a currently visible row near the retention cut, and GoalTerminal only clears a missing pending anchor, causing a jump. Implement an explicit retention/viewport contract so the currently visible anchor row and pixel offset remain stable through multiple trims and older-page loads while total retained events stays finite. Preserve authoritative live tail, dedupe, exclusive cursors, gap recovery, follow-tail behavior, and concurrent live events. Define deterministic fallback only when the anchor is genuinely unavailable because of empty/rejected/stale data. Add an adversarial composed test: begin with >1,000 retained events, navigate to a historical mounted window near the trim boundary, force multiple older-page loads/trims plus live arrivals, and assert the same visible row and offset after each; also cover empty/rejected/stale load cleanup without dangling anchor state.

  2. Remove the three blanket max-lines suppressions introduced at the top of GoalDetails.test.tsx, useGoalDetail.test.tsx, and GoalsPage.test.tsx. Split reusable fixtures/harnesses and test groups into focused files/modules so every file satisfies the repository's 400-line rule without disable comments. Preserve all coverage and add no new lint/type/test suppressions.

Run the focused adversarial tests, complete UI suite, UI/root typecheck/lint/build, git diff --check, and suppression scan. Publish one new exact head for re-audit; do not merge.

@propr-dev

propr-dev Bot commented Aug 31, 2026

Copy link
Copy Markdown
Contributor

Applied the requested follow-up changes in commit 3c3585e

Summary of Changes

Implemented both blockers in the PR worktree, leaving it uncommitted and unmerged as required.

  • Added an explicit viewport-anchor retention contract preserving the visible event sequence and pixel offset across repeated trims, older-page loads, gap recovery, and concurrent live arrivals.
  • Added deterministic cleanup/fallback coverage for empty, rejected, stale, and genuinely unavailable anchors.
  • Split all three oversized test files into focused suites and reusable harness modules. Largest affected test file is 335 lines; no max-lines disables or new suppressions remain.
  • Adversarial retention test verifies three 200-event loads, concurrent live events, stable row/offset, and the 1,000-event bound.

Validation passed:

  • Focused adversarial terminal suite: 15/15
  • Complete UI suite: 77 files, 621 tests
  • UI lint, typecheck, and build
  • Root lint, typecheck, and build
  • git diff --check
  • Suppression and trailing-whitespace scans

Current HEAD remains 72bb909d17e1691800297ce265fef9b4d7d7f0d9; the requested new exact head will be created by the surrounding commit system.


🤖 Implementation Details

  • Model: GPT-5.6 Sol (high)
  • Requested By: @integry
  • Time: 28m 19s
  • Tokens: 7,284,396 (7,248,867 in / 35,529 out)
  • Cost: $5.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: 5481686357✓

@integry integry removed the AI label Aug 31, 2026
…committed and unmerged as required.

Implemented both blockers in the PR worktree, leaving it uncommitted and unmerged as required.

- Added an explicit viewport-anchor retention contract preserving the visible event sequence and pixel offset across repeated trims, older-page loads, gap recovery, and concurrent live arrivals.
- Added deterministic cleanup/fallback coverage for empty, rejected, stale, and genuinely unavailable anchors.
- Split all three oversized test files into focused suites and reusable harness modules. Largest affected test file is 335 lines; no max-lines disables or new suppressions remain.
- Adversarial retention test verifies three 200-event loads, concurrent live events, stable row/offset, and the 1,000-event bound.

Validation passed:

- Focused adversarial terminal suite: 15/15
- Complete UI suite: 77 files, 621 tests
- UI lint, typecheck, and build
- Root lint, typecheck, and build
- `git diff --check`
- Suppression and trailing-whitespace scans

Current `HEAD` remains `72bb909d17e1691800297ce265fef9b4d7d7f0d9`; the requested new exact head will be created by the surrounding commit system.

PR: #2020
Comment by: @integry (ID: 5481686357)
Model: gpt-5.6-sol
@integry integry added the AI label Aug 31, 2026
@integry

integry commented Aug 31, 2026

Copy link
Copy Markdown
Owner Author

Owner full integrated re-audit at exact 3c3585e found one remaining HIGH long-gap reconciliation blocker. Keep this draft sub-epic PR unmerged and fix it with Codex GPT-5.6 Sol only, preserving the now-verified viewport anchor, finite retention, split no-suppression suites, and all prior six corrections.

A gap replay larger than 1,000 events is trimmed to the retained display tail before useGoalDetail scans for lifecycle/message/usage changes. If the relevant lifecycle, message, or usage event is near the beginning and the later retained tail is only stdout, the scan sees no detail-changing event, skips prompt authoritative getGoal, and leaves lifecycle/messages/stats stale until the 30-second probe.

Make replay/drain recovery propagate a bounded detailChanged/authoritative-refresh summary independent of retained display events. Accumulate the relevant event-type signal while each replay page is observed, before retention eviction, and carry it through cursor/gap completion under the same goal identity and mutation-revision fence. Trigger at most one prompt authoritative detail refresh per completed recovery window when any page observed a relevant change; do not create refresh loops, duplicate replay, or allow stale recovery to overwrite a newer mutation/identity. Preserve exact event tail/cap, dedupe, exclusive cursor, gap fencing, follow-tail, viewport anchor/offset, concurrent live arrival, and reset behavior.

Add an adversarial >1,000-event gap test with lifecycle + message + usage events near the beginning and stdout-only retained tail. Assert prompt bounded getGoal refresh, updated lifecycle/messages/stats, exactly-once/coalesced refresh, no replay loop or duplicates, correct identity/revision fencing when a mutation or goal switch interleaves, and unchanged 1,000-event cap/tail/anchor. Run focused gap/retention tests, complete UI suite, UI/root typecheck/lint/build, suppression scan, and git diff --check. Publish one exact head; do not merge.

@propr-dev

propr-dev Bot commented Aug 31, 2026

Copy link
Copy Markdown
Contributor

Applied the requested follow-up changes in commit 1448eae

Summary of Changes

Implemented the long-gap reconciliation fix without committing or merging.

Key changes:

  • Replay now accumulates a bounded detailChanged signal before event retention eviction.
  • Recovery carries that signal across all replay pages and coalesced gap targets.
  • Each completed recovery window triggers at most one prompt detail reconciliation.
  • Existing identity and mutation-revision fences remain enforced.
  • Added >1,000-event adversarial coverage for lifecycle/message/usage eviction, exact cap/tail, anchor retention, dedupe, refresh coalescing, mutation fencing, and goal-switch fencing.

Validation passed:

  • Focused gap/retention suites: 48 tests
  • Complete UI suite: 77 files, 623 tests
  • UI lint, typecheck, build
  • Root lint, typecheck, build
  • Suppression scan
  • git diff --check

Only five scoped files changed. HEAD remains unmerged at 3c3585ecf430d0ea7472a7fcba4a22fdfc5d858f; the surrounding system can publish the resulting exact head.


🤖 Implementation Details

  • Model: GPT-5.6 Sol (high)
  • Requested By: @integry
  • Time: 15m 3s
  • Tokens: 2,320,488 (2,301,817 in / 18,671 out)
  • Cost: $2.16
  • 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: 5482134294✓

@integry integry removed the AI label Aug 31, 2026
…ing or merging.

Implemented the long-gap reconciliation fix without committing or merging.

Key changes:

- Replay now accumulates a bounded `detailChanged` signal before event retention eviction.
- Recovery carries that signal across all replay pages and coalesced gap targets.
- Each completed recovery window triggers at most one prompt detail reconciliation.
- Existing identity and mutation-revision fences remain enforced.
- Added >1,000-event adversarial coverage for lifecycle/message/usage eviction, exact cap/tail, anchor retention, dedupe, refresh coalescing, mutation fencing, and goal-switch fencing.

Validation passed:

- Focused gap/retention suites: 48 tests
- Complete UI suite: 77 files, 623 tests
- UI lint, typecheck, build
- Root lint, typecheck, build
- Suppression scan
- `git diff --check`

Only five scoped files changed. HEAD remains unmerged at `3c3585ecf430d0ea7472a7fcba4a22fdfc5d858f`; the surrounding system can publish the resulting exact head.

PR: #2020
Comment by: @integry (ID: 5482134294)
Model: gpt-5.6-sol
@integry integry removed the AI label Aug 31, 2026
@propr-dev

propr-dev Bot commented Aug 31, 2026

Copy link
Copy Markdown
Contributor

🔍 AI Code Review — gpt-5.6-sol

Overall Evaluation

This PR adds a substantial Goals operator experience with capability-aware creation, cursor-based listing, realtime invalidation, and detailed lifecycle controls. The visible implementation is thoughtfully tested, but it needs minor correctness fixes before its eventual integrated merge; the objective also explicitly prohibits merging it independently. This review is partial because the supplied diff omitted some files/ranges, and the full test suite remains pending.

Fail-closed capability selection — Goal creation intersects explicitly goal-capable models with supported models and excludes disabled or malformed catalog entries.

Strong stale-data fencing — List requests are scoped to authorization and query fingerprints, abort superseded requests, and discard stale cross-owner completions.

Accessible operator controls — The changed UI includes route landmarks, labeled status regions, modal focus management, read-only gating, and explicit lifecycle/message states.

Merge blockers

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

F1: 🔴 Edit-revert can duplicate goals

  • Required behavior: Retrying a potentially committed create mutation with an identical final payload must reuse its idempotency key so an uncertain response cannot produce a duplicate goal.
  • Evidence: propr-ui/src/pages/useGoalCreateForm.ts:59beginNewIntent immediately rotates the key and clears lastAttemptRef on any field edit. If an operator edits a field after an uncertain response and then restores its original value, the final serialized payload is identical, but the original key has already been discarded.
  • Minimum fix: Preserve the last attempted payload and key across edits. At submission time, reuse that key when the final serialized payload matches the last attempt, and rotate it only when the submitted payload actually differs; add an edit-then-revert regression test.

F2: 🔴 Unicode limits are inconsistent

  • Required behavior: The new 4,000-character objective and message bounds must be enforced consistently across form input and API validation, including astral Unicode characters.
  • Evidence: propr-ui/src/pages/GoalCreateForm.tsx:145 and propr-ui/src/components/GoalDetails/GoalControls.tsx:67 — the textareas use HTML maxLength={4000}, which counts UTF-16 code units, while validateGoalForm, sendGoalMessage, and the displayed message counter use Array.from(...).length, which counts Unicode code points. Consequently, only 2,000 emoji can be entered even though the validation contract permits 4,000.
  • Minimum fix: Enforce these limits using the same unit throughout. Given the existing API validation, remove the native maxLength restriction and bound input changes by Array.from(value).slice(0, 4000).join(''), or otherwise make the entire contract consistently code-unit-based.

Suggestions

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

S1: 🟢 Abort catalog loads

getInstanceCatalog now accepts an abort signal, but useGoalCreateForm only uses a cancellation boolean. Passing an AbortController signal would stop unnecessary catalog work when the route or authenticated identity unmounts. The current guard already prevents stale state commits, so this is resource hardening rather than a merge blocker.

S2: 🟢 Tighten cursor history

propr-ui/src/pages/goalsUrlState.ts rejects null entries after the first position but accepts histories whose first entry is non-null, even though generated navigation always begins with null. Rejecting that noncanonical shape would prevent misleading page numbers and an irreversible boundary on manually edited URLs; normal application-generated navigation is already correct.

Score

The implementation is broad and well tested, but the idempotency flaw can duplicate a goal after an uncertain mutation, and Unicode bounds reject contract-valid input. The omitted diff ranges, explicit integration dependency, and pending full suite also limit final verification.

Score: 6/10


🤖 Review Details

  • Model: GPT-5.6 Sol
  • Time: 6m 49s
  • Tokens: 88,873 (77,665 in / 11,208 out)
  • Cost: $0.69
  • Review scope: Partial — PR diff files or ranges were unavailable from GitHub or omitted by the configured review context limit.

View Task

Files omitted from review diff

1 file was omitted because patch content was unavailable from GitHub or did not fit the review context budget. Large, binary, generated, and lockfile changes are deprioritized.

  • propr-ui/src/components/GoalDetails/useGoalDetail.ts

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

🤖 Review by ProPR

@integry integry added the AI label Aug 31, 2026
@integry

integry commented Aug 31, 2026

Copy link
Copy Markdown
Owner Author

/fix F1 F2

@propr-dev

propr-dev Bot commented Aug 31, 2026

Copy link
Copy Markdown
Contributor

Applied the requested follow-up changes in commit 484efb7

Addressed 1 AI review comment (IDs: 5482580626)

Summary of Changes

Implemented only F1 and F2.

  • F1: Preserved the previous payload/key across edits; submission now rotates only when the final payload differs. Added edit-then-revert coverage in GoalCreatePage.test.tsx.
  • F2: Objective and message inputs now clamp to 4,000 Unicode code points and no longer use UTF-16-based maxLength. Added 4,001-emoji regression tests.

Verification passed:

  • 77 test files, 626 tests
  • TypeScript typecheck
  • ESLint on all touched files
  • git diff --check

No commit was created.


🤖 Implementation Details

  • Model: GPT-5.6 Sol (high)
  • Requested By: @integry
  • Time: 5m 53s
  • Tokens: 703,192 (697,243 in / 5,949 out)
  • Cost: $0.81
  • 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: 5482588682✓

@integry integry removed the AI label Aug 31, 2026
Implemented only F1 and F2.

- F1: Preserved the previous payload/key across edits; submission now rotates only when the final payload differs. Added edit-then-revert coverage in [GoalCreatePage.test.tsx](/tmp/git-processor/worktrees/integry/propr/pr-2020-followup-2026-08-31T18-18-55/propr-ui/src/pages/GoalCreatePage.test.tsx).
- F2: Objective and message inputs now clamp to 4,000 Unicode code points and no longer use UTF-16-based `maxLength`. Added 4,001-emoji regression tests.

Verification passed:

- 77 test files, 626 tests
- TypeScript typecheck
- ESLint on all touched files
- `git diff --check`

No commit was created.

PR: #2020
Comment by: @integry (ID: 5482588682)
Model: gpt-5.6-sol
@integry integry added the AI label Aug 31, 2026
@integry

integry commented Aug 31, 2026

Copy link
Copy Markdown
Owner Author

Owner exact-head correction at 484efb771bd72c6940857677b87e6e49046287d7: F1 is clean; fix only the remaining F2 behavior with Codex GPT-5.6 Sol and keep the draft PR unmerged.

The current first-4,000-code-point onChange slicing is destructive: inserting/pasting/composing before the end of a full draft silently drops existing trailing code points. It also clamps before trimming while validation/submission use the trimmed value, so one leading space + 4,000 valid characters becomes 3,999 sent characters and the steering counter disagrees with the payload.

Use a non-destructive, composition-safe contract for both the objective and steering message:

  • Preserve the raw draft on input/change/paste/composition; do not silently slice or delete previously accepted text. Keep native UTF-16 maxLength absent.
  • Derive the limit and visible counter from the exact trimmed Unicode-code-point value used by validation/API submission (Array.from(trimmed).length or a shared helper). If the effective value exceeds 4,000, show actionable validation and prevent submit/send while preserving the draft; 4,000 astral code points remains valid.
  • Preserve leading/trailing whitespace in the editable draft until submit/send, but send the canonical trimmed value exactly once. Do not alter existing retry/idempotency keys, pending-message behavior, error draft preservation, or IME composition.
  • Add adversarial regressions for both fields: prepend/middle/append insertion into a full draft without suffix loss, over-limit paste, leading/trailing whitespace around exactly 4,000 valid code points, astral and combining sequences, composition events, counter/validation/API payload agreement, and recovery after editing back under limit.

Retain F1 and all prior replay/list/detail/auth/socket/viewport/terminal/accessibility/security behavior. Run focused tests, full UI suite, UI/root typecheck/lint/build, git diff --check, and suppression scan. Publish one exact head; do not merge.

@propr-dev

propr-dev Bot commented Aug 31, 2026

Copy link
Copy Markdown
Contributor

Applied the requested follow-up changes in commit cc7beaa

Summary of Changes

Implemented only F2; F1 remains untouched.

  • Objective and steering drafts now preserve raw input, paste, and IME composition without slicing or maxLength.
  • Counters and validation use trimmed Unicode code-point length via canonicalGoalText.ts.
  • Over-limit drafts remain editable, show actionable errors, and cannot submit.
  • API payloads contain the canonical trimmed value.
  • Added adversarial regressions for insertion positions, paste, whitespace, astral/combining characters, composition, payload agreement, and recovery.

Validation passed:

  • Focused: 83 tests
  • Full UI: 77 files, 632 tests
  • UI/root typecheck, lint, and build
  • git diff --check
  • Suppression scan

HEAD remains 484efb771bd72c6940857677b87e6e49046287d7. No commit, merge, or PR state change was performed.


🤖 Implementation Details

  • Model: GPT-5.6 Sol (high)
  • Requested By: @integry
  • Time: 15m 7s
  • Tokens: 1,972,512 (1,952,322 in / 20,190 out)
  • Cost: $2.06
  • 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: 5482758347✓

@integry integry removed the AI label Aug 31, 2026
Implemented only F2; F1 remains untouched.

- Objective and steering drafts now preserve raw input, paste, and IME composition without slicing or `maxLength`.
- Counters and validation use trimmed Unicode code-point length via [canonicalGoalText.ts](/tmp/git-processor/worktrees/integry/propr/pr-2020-followup-2026-08-31T18-34-24/propr-ui/src/utils/canonicalGoalText.ts:1).
- Over-limit drafts remain editable, show actionable errors, and cannot submit.
- API payloads contain the canonical trimmed value.
- Added adversarial regressions for insertion positions, paste, whitespace, astral/combining characters, composition, payload agreement, and recovery.

Validation passed:

- Focused: 83 tests
- Full UI: 77 files, 632 tests
- UI/root typecheck, lint, and build
- `git diff --check`
- Suppression scan

HEAD remains `484efb771bd72c6940857677b87e6e49046287d7`. No commit, merge, or PR state change was performed.

PR: #2020
Comment by: @integry (ID: 5482758347)
Model: gpt-5.6-sol
@integry integry added the AI label Aug 31, 2026
@integry

integry commented Aug 31, 2026

Copy link
Copy Markdown
Owner Author

/review gpt-5.6-sol

@propr-dev

propr-dev Bot commented Aug 31, 2026

Copy link
Copy Markdown
Contributor

AI Code Review Complete requested by @integry

Posted 1 review:

View Task Details

@integry integry removed the AI label Aug 31, 2026
@propr-dev

propr-dev Bot commented Aug 31, 2026

Copy link
Copy Markdown
Contributor

🔍 AI Code Review — gpt-5.6-sol

Overall Evaluation

This PR delivers a substantial Goals operator UI with creation, filtering, pagination, replay, lifecycle controls, steering, hierarchy, and statistics. However, it is not ready to merge independently: the catalog integration currently disables creation for every agent, and goal-detail reads do not enforce response identity. The supplied diff is explicitly partial, and the full test suite remains pending.

Robust list state — Search, filters, and bounded cursor history are canonicalized in the URL, while stale and cross-authorization requests are fenced.

Resilient operator experience — Replay enforces contiguous sequences, terminal retention is bounded, and realtime invalidations are coalesced with reconnect refreshes.

Strong interaction coverage — The changed tests exercise Unicode limits, idempotency behavior, modal focus, demo-mode restrictions, pagination, and stale-response handling.

Merge blockers

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

F3: 🔴 Creation is disabled for every agent

  • Required behavior: The Goals create workflow must expose agents and models explicitly enabled for goal execution, and the objective requires its capability-provider integration before merge.
  • Evidence: packages/api/routes/instanceCatalogRoutes.ts:36 and propr-ui/src/pages/useGoalCreateForm.ts:54 — the catalog hardcodes goalCapable: false and an empty model list for every agent, while the form filters out precisely those agents, leaving no valid creation path.
  • Minimum fix: Populate these fields from the integrated agent capability discriminator and intersect the configured goal-capable models with supportedModels, rather than returning unconditional false/empty values.

F4: 🔴 Goal reads do not verify identity

  • Required behavior: A goal detail route must fail closed when its response identifies a different goal; otherwise the operator can view one goal while lifecycle mutations target another.
  • Evidence: propr-ui/src/api/goalDetailApi.ts:11getGoal(goalId) decodes and returns the response without checking detail.goal.goalId === goalId, despite mutation responses and replay events enforcing the corresponding identity invariant.
  • Minimum fix: Decode into a local variable, compare its goal ID with the requested ID, and throw GoalContractError on mismatch before exposing the detail to the UI.

Suggestions

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

S1: 🟢 Abort catalog loading

getInstanceCatalog now accepts an AbortSignal, but useGoalCreateForm only uses a cancellation boolean. Passing a controller signal would stop unnecessary network and decode work when the creation route unmounts; the current guard already prevents stale state updates, so this is optional cleanup.

S2: 🟢 Index hierarchy dependencies

GoalHierarchy filters the complete dependency collection once per node. Pre-indexing dependencies by nodeId would avoid quadratic rendering costs for large goal trees, but it is a performance improvement rather than a correctness requirement.

Score

The PR has thoughtful implementation and extensive targeted coverage, with no current check failure, but the creation workflow is unusable on this head and goal-detail identity is not safely enforced. The pending full suite and explicitly truncated review input add further verification uncertainty.

Score: 5/10


🤖 Review Details

  • Model: GPT-5.6 Sol
  • Time: 5m 4s
  • Tokens: 85,925 (77,596 in / 8,329 out)
  • Cost: $0.59
  • Review scope: Partial — PR diff files or ranges were unavailable from GitHub or omitted by the configured review context limit.

View Task

Files omitted from review diff

2 files were omitted because patch content was unavailable from GitHub or did not fit the review context budget. Large, binary, generated, and lockfile changes are deprioritized.

  • propr-ui/src/components/GoalDetails/GoalTerminal.test.tsx
  • propr-ui/src/components/GoalDetails/useGoalDetail.ts

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

🤖 Review by ProPR

@integry integry added the AI label Aug 31, 2026
@integry

integry commented Aug 31, 2026

Copy link
Copy Markdown
Owner Author

/fix F4

F3 is intentionally deferred to the integrated control-plane/catalog capability provider and is a fail-closed cross-sub-epic gate tracked in #2015; do not fabricate goal-capable agents or models on this isolated UI branch. Fix only F4 with an exact requested/decoded goal identity check, typed GoalContractError, and direct mismatch/success regressions. Preserve all existing behavior and keep the PR unmerged.

@propr-dev

propr-dev Bot commented Aug 31, 2026

Copy link
Copy Markdown
Contributor

Applied the requested follow-up changes in commit 37b79a3

Addressed 1 AI review comment (IDs: 5483113438)

Summary of Changes

Implemented F4 only.

  • getGoal now decodes locally and verifies the decoded goal ID exactly matches the requested ID.
  • Mismatches throw typed GoalContractError.
  • Added mismatch and exact-match success regression coverage.
  • F3 remains untouched; no commit or merge performed.

Verification passed:

  • API tests: 51/51
  • TypeScript typecheck
  • ESLint
  • git diff --check

🤖 Implementation Details

  • Model: GPT-5.6 Sol (high)
  • Requested By: @integry
  • Time: 2m 44s
  • Tokens: 210,296 (207,826 in / 2,470 out)
  • Cost: $0.28

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

@integry integry removed the AI label Aug 31, 2026
Implemented F4 only.

- `getGoal` now decodes locally and verifies the decoded goal ID exactly matches the requested ID.
- Mismatches throw typed `GoalContractError`.
- Added mismatch and exact-match success regression coverage.
- F3 remains untouched; no commit or merge performed.

Verification passed:

- API tests: 51/51
- TypeScript typecheck
- ESLint
- `git diff --check`

PR: #2020
Comment by: @integry (ID: 5483179703)
Model: gpt-5.6-sol
@integry

integry commented Aug 31, 2026

Copy link
Copy Markdown
Owner Author

Owner exact-head validation is CLEAN at 37b79a3c5245f7d992e3c12aab1b0730dcb3cad3.

  • F4 now strictly decodes the response, compares detail.goal.goalId byte-for-byte with the originally requested decoded ID, and throws typed GoalContractError before exposure on mismatch.
  • Direct encoded-ID success and mismatched-ID rejection regressions pass.
  • Prior F1/F2, replay/auth/socket/retention/viewport/terminal/accessibility/security invariants are unchanged.
  • Independent focused validation passed 138 tests; full UI validation passed 77 files / 633 tests; exact-head Validate Changes and compatibility checks are green.

F3 remains intentionally fail-closed on this isolated UI branch and is an explicit #2015 integration gate: the control-plane/catalog capability provider must supply real goal-capable agents/models. Keep this sub-epic draft unmerged until those cross-sub-epic contracts are integrated.

@integry

integry commented Aug 31, 2026

Copy link
Copy Markdown
Owner Author

Forced exact-head Full Test Suite run 33430054292 is now green on retry without any code change.

The first attempt timed out only in the existing replay-pagination test bounds repeated older pages without losing a live tail delivered during the load (expected the third paged call within its wait window); the same exact SHA then passed the complete repository suite, matching the independent local 633-test UI pass. Carry this timing-sensitive case into #2015 soak validation and fix it if it reproduces there; it is not currently a functional exact-head blocker.

@integry integry added the AI label Aug 31, 2026
@integry

integry commented Aug 31, 2026

Copy link
Copy Markdown
Owner Author

Execute one narrow test-only Codex GPT-5.6 Sol correction at exact head 37b79a3c5245f7d992e3c12aab1b0730dcb3cad3; keep this sub-epic draft and unmerged. Do not change the production hook/product behavior and do not implement deferred F3.

Forced Full Test Suite run 33430054292 attempt 1 exposed a deterministic harness synchronization race in useGoalDetail.test.tsx case bounds repeated older pages without losing a live tail delivered during the load; attempt 2 passed unchanged. The test waits only for getGoalEvents call count. Because the call begins synchronously, that wait can finish before the promise continuation clears loadingOlder; the harness leaves its older button enabled, so the next synthetic click is intentionally dropped by the hook's busy guard and can never be recovered by a longer timeout. The real GoalTerminal disables the control while loading.

Fix only the test/harness contract:

  • Make the harness older-page control mirror production busy/availability semantics by exposing and honoring loadingOlder (and hasMoreBefore where appropriate).
  • In the repeated-page test, wait for each page to finish—observable cursor/earliest-event update plus control re-enabled / loadingOlder === false—before the next click.
  • Preserve exact cursor sequencing, the 1,000-event cap, live-tail 1401, anchor/viewport, gap, auth, and dedupe assertions.
  • Add a bounded stress repetition that would reliably catch the old dropped-click race without inflating arbitrary waitFor timeouts.
  • Keep the delta test-only unless the existing test harness needs a typed state exposure; no production behavior change, no suppressions.
  • Run the focused replay suite repeatedly, full 633-test UI suite, typecheck/lint/build, and git diff --check; publish one exact head and stop without merging.

@propr-dev

propr-dev Bot commented Aug 31, 2026

Copy link
Copy Markdown
Contributor

Applied the requested follow-up changes in commit b9e41cf

Summary of Changes

Implemented the narrow test-only correction at base HEAD 37b79a3c5245f7d992e3c12aab1b0730dcb3cad3.

  • Harness now exposes loadingOlder/hasMoreBefore and disables older pagination while busy or unavailable.
  • Replay test waits for earliest-event advancement, idle state, and control re-enablement.
  • Added five bounded scenario repetitions and exact cursor/dedupe/retention/live-tail assertions.
  • No production files or behavior changed; F3 untouched.

Validation passed:

  • Focused replay suite: 10 consecutive runs
  • Shared replay/race suite: 22 tests
  • Full UI suite: 77 files / 633 tests
  • Typecheck, lint, build
  • git diff --check

Changed only useGoalDetail.test.tsx and useGoalDetailTestHarness.tsx. No commit, push, or merge performed.


🤖 Implementation Details

  • Model: GPT-5.6 Sol (high)
  • Requested By: @integry
  • Time: 8m 21s
  • Tokens: 799,616 (790,839 in / 8,777 out)
  • Cost: $0.90

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

@integry integry removed the AI label Aug 31, 2026
…7b79a3c5245f7d992e3c12aab1b0730dcb3cad3`.

Implemented the narrow test-only correction at base HEAD `37b79a3c5245f7d992e3c12aab1b0730dcb3cad3`.

- Harness now exposes `loadingOlder`/`hasMoreBefore` and disables older pagination while busy or unavailable.
- Replay test waits for earliest-event advancement, idle state, and control re-enablement.
- Added five bounded scenario repetitions and exact cursor/dedupe/retention/live-tail assertions.
- No production files or behavior changed; F3 untouched.

Validation passed:

- Focused replay suite: 10 consecutive runs
- Shared replay/race suite: 22 tests
- Full UI suite: 77 files / 633 tests
- Typecheck, lint, build
- `git diff --check`

Changed only [useGoalDetail.test.tsx](/tmp/git-processor/worktrees/integry/propr/pr-2020-followup-2026-08-31T19-48-39/propr-ui/src/components/GoalDetails/useGoalDetail.test.tsx:168) and [useGoalDetailTestHarness.tsx](/tmp/git-processor/worktrees/integry/propr/pr-2020-followup-2026-08-31T19-48-39/propr-ui/src/components/GoalDetails/useGoalDetailTestHarness.tsx:65). No commit, push, or merge performed.

PR: #2020
Comment by: @integry (ID: 5483638830)
Model: gpt-5.6-sol
@integry

integry commented Aug 31, 2026

Copy link
Copy Markdown
Owner Author

Owner exact-head revalidation is CLEAN at b9e41cfbefea01b60e898800f9f71c94a0647c69.

  • Delta is strictly the two test/harness files; no production behavior changed and deferred F3 remains untouched.
  • Harness now mirrors production loadingOlder || !hasMoreBefore disabling.
  • The regression waits for each committed earliest sequence, idle state, availability, and re-enabled control before the next click—closing the dropped-click race without longer arbitrary timeouts.
  • Exact cursor sequence, 1,000 cap/uniqueness, single live 1401, old/tail retention, and pending-page live delivery are all asserted.
  • Independent stress: 20 Vitest processes / 100 complete cycles; focused 138 tests; full UI 77 files / 633 tests.
  • Exact-head Validate/Build/compat and forced Full Test Suite run 33433556290 are green.

Keep this sub-epic draft unmerged until #2015 integrates the real control-plane/catalog/runtime contracts.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant