Skip to content

[2621 by GPT-6 Astra] Make dashboard outcomes incremental and load earlier updates on demand - #2630

Merged
integry merged 5 commits into
mainfrom
2621/gpt-6-astra-make-dashboard-outcomes-i-20260929-1156-lmt
Sep 30, 2026
Merged

integry merged 5 commits into
mainfrom
2621/gpt-6-astra-make-dashboard-outcomes-i-20260929-1156-lmt

Conversation

@propr-dev

@propr-dev propr-dev Bot commented Sep 29, 2026 •

Copy link
Copy Markdown
Contributor

AI Implementation Summary

Closes #2621

Branch: 2621/gpt-6-astra-make-dashboard-outcomes-i-20260929-1156-lmt
Commits: ✅ Changes committed (42c6405)


AI Processing Completed

Execution Details:

Summary:
Implemented durable outcome projections, backward-compatible summary/history APIs, and expansion-only history loading with pagination, retries, and live updates. Added rebuild/rollback documentation and visual previews.

Validated 72 focused tests, typechecks, parity/recovery checks, browser behavior, and compiled-worker execution.

Staging latency and load targets still require measurement.

Detailed Logs:

  • Conversation: 208 messages
  • Session: 01a0ed06-9f6a-7c52-923b-c45486d09817

Log files stored at:

  • conversation: /tmp/claude-logs/issue-2621-2026-09-29T13-09-33-992Z-conversation.json
  • output: /tmp/claude-logs/issue-2621-2026-09-29T13-09-33-992Z-output.txt
Latest Conversation Messages

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


💡 Need changes?

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


Visual preview

Paginated earlier updates

Paginated earlier updates

The first page of earlier updates with a local Load more updates control.

Original unavailable: managed storage is unavailable or the upload has expired.

Earlier updates loading

Earlier updates loading

Expanded entity loads its history without blocking the completed feed.

Original unavailable: managed storage is unavailable or the upload has expired.

…d load earli

Implemented by ProPR AI using gpt-6-astra model.

Implementation completed successfully.
Comment thread packages/api/routes/dashboardOutcomeQueries.ts Fixed
@propr-dev

propr-dev Bot commented Sep 29, 2026

Copy link
Copy Markdown
Contributor Author

CI failed: CodeQL

Please investigate and fix this CI failure.

Failure evidence

packages/api/routes/dashboardOutcomeQueries.ts:569 — Type confusion through parameter tampering
Potential type confusion as this HTTP request parameter may be either an array or a string.

@propr-dev

propr-dev Bot commented Sep 29, 2026 •

Copy link
Copy Markdown
Contributor Author

✅ Applied the requested follow-up changes in commit 3cfd794

Summary of Changes

Added a runtime string check before cursor length validation and decoding, addressing the reported type confusion.

Validated with 19 existing tests, targeted cursor regression checks, and the API typecheck. No visual changes or previews.


🤖 Implementation Details

  • Model: GPT-6 Astra (high)
  • Requested By: @github-advanced-security[bot]
  • Time: 2m 46s
  • Tokens: 312,384 (310,028 in / 2,356 out)
  • Cost: $0.69

Undo Changes • View Task Execution


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

🤖 Generated by ProPR
Processing comment ID: 4133865985✓

…n and decoding, addressing the reported type confusion.

Added a runtime string check before cursor length validation and decoding, addressing the reported type confusion.

Validated with 19 existing tests, targeted cursor regression checks, and the API typecheck. No visual changes or previews.

PR: #2630
Comment by: @github-advanced-security[bot] (ID: 4133865985)
Model: gpt-6-astra
@propr-dev

propr-dev Bot commented Sep 29, 2026 •

Copy link
Copy Markdown
Contributor Author

ℹ️ Analyzed the follow-up request by @propr-dev[bot]

Analysis Summary

The fix is already present on this branch: cursor validation checks its runtime type before accessing its length or decoding it. No additional changes were needed.

Verified:

  • 19 existing tests passed.
  • Nine malformed cursor inputs were rejected; valid pagination worked.
  • API typecheck passed.

The reported failure is from the earlier commit; CodeQL still needs to run on the current revision.

No code changes were necessary based on the current state of the branch.


🤖 Analysis Details

  • Model: GPT-6 Astra (high)
  • Time: 2m 46s
  • Tokens: 294,864 (292,829 in / 2,035 out)
  • Cost: $0.74

View Task Execution


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

🤖 Generated by ProPR
Processing comment ID: 5891040574✓

@propr-dev

propr-dev Bot commented Sep 29, 2026

Copy link
Copy Markdown
Contributor Author

CI failed: Validate Changes

Please investigate and fix this CI failure.

Failure evidence

.github:8
Process completed with exit code 1.

.github:360
Process completed with exit code 1.

.github:313 — 🎭 Playwright Run Summary
133 passed (6.0m)

@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 36573990489, attempt 1
Checkout outcome: success

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

--- Release shell script lint ---

--- Toolchain ---
v22.23.2
10.9.8

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

added 1119 packages, and audited 1128 packages in 20s

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

found 0 vulnerabilities

--- CLI release package ---
Verifying propr-cli release package...

> propr@0.8.15 cli:pack
> node packages/cli/scripts/build-publish.mjs


> @propr/shared@0.8.15 build
> tsc


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


> @propr/cli@0.8.15 build
> tsc && node scripts/copy-assets.mjs

copy-assets: copied 9 file(s) into the CLI package.

Staged propr-cli@0.8.15 at /home/runner/work/propr/propr/dist-publish/propr-cli
npm notice
npm notice 📦  propr-cli@0.8.15
npm notice Tarball Contents
npm notice 25.7kB README.md
npm notice 36.3kB dist/agentSkill.js
npm notice 754B dist/api/agentRuntime.js
npm notice 7.1kB dist/api/agents.js
npm notice 1.2kB dist/api/agentTank.js
npm notice 10.9kB dist/api/client.js
npm notice 4.7kB dist/api/errors.js
npm notice 5.4kB dist/api/implement.js
npm notice 2.0kB dist/api/index.js
npm notice 1.9kB dist/api/logs.js
npm notice 5.7kB dist/api/plans.js
npm notice 4.2kB dist/api/relay.js
npm notice 10.4kB dist/api/repos.js
npm notice 11.1kB dist/api/settings.js
npm notice 1.6kB dist/api/syntheticPools.js
npm notice 1.8kB dist/api/system.js
npm notice 5.8kB dist/api/tasks.js
npm notice 2.9kB dist/api/todos.js
npm notice 86B dist/api/types.js
npm notice 313B dist/api/visualPreviewAuth.js
npm notice 26.1kB dist/assets/env.example.txt
npm notice 5.7kB dist/auth/githubLogin.js
npm notice 19.5kB dist/commands/agentCommands.js
npm notice 4.2kB dist/commands/agentPoolCommands.js
npm notice 4.3kB dist/commands/agentSkillCommands.js
npm notice 28.7kB dist/commands/agentValidation.js
npm notice 65.2kB dist/commands/checkCommands.js
npm notice 8.9kB dist/commands/configCommands.js
npm notice 14.6kB dist/commands/connectCommand.js
npm notice 2.4kB dist/commands/imageCommands.js
npm notice 7.2kB dist/commands/implementCommands.js
npm notice 1.6kB dist/commands/index.js
npm notice 6.0kB dist/commands/initCommands.js
npm notice 14.1kB dist/commands/initStack.js
npm notice 6.1kB dist/commands/logCommands.js
npm notice 24.9kB dist/commands/planCommands.js
npm notice 8.4kB dist/commands/relayCommands.js
npm notice 30.1kB dist/commands/repoCommands.js
npm notice 7.5kB dist/commands/runtimeCommands.js
npm notice 16.1kB dist/commands/settingCommands.js
npm notice 4.1kB dist/commands/setup/agentHostActions.js
npm notice 123B dist/commands/setup/agents.js
npm notice 4.5kB dist/commands/setup/engine.js
npm notice 51B dist/commands/setup/github.js
npm notice 15.3kB dist/commands/setup/hostActions.js
npm notice 25.3kB dist/commands/setup/sequential.js
npm notice 51B dist/commands/setup/state.js
npm notice 51B dist/commands/setup/types.js
npm notice 10.7kB dist/commands/setupCommand.js
npm notice 4.4kB dist/commands/stackCommands.js
npm notice 1.4kB dist/commands/startCommand.js
npm notice 13.5kB dist/commands/systemCommands.js
npm notice 2.2kB dist/commands/tankCommands.js
npm notice 43.2kB dist/commands/taskCommands.js
npm notice 29.6kB dist/commands/todoCommands.js
npm notice 31.0kB dist/commands/tunnelCommand.js
npm notice 2.7kB dist/commands/uiDocsCommands.js
npm notice 8.3kB dist/completion.js
npm notice 20.0kB dist/config/ConfigManager.js
npm notice 276B dist/config/index.js
npm notice 989B dist/config/rootKey.js
npm notice 345B dist/config/types.js
npm notice 42.9kB dist/connectIdentity.js
npm notice 25.0kB dist/connectRootAuthority.js
npm notice 32.5kB dist/connectWindowsAuthority.js
npm notice 2.2kB dist/desktopDiscovery.js
npm notice 12.4kB dist/desktopLocalSetup.js
npm notice 16.3kB dist/index.js
npm notice 4.5kB dist/native/darwin-authority-broker.c
npm notice 8.9kB dist/native/directory-operations.c
npm notice 50.1kB dist/native/prebuilds/darwin-arm64/connect-authority-broker
npm notice 54.2kB dist/native/prebuilds/darwin-arm64/directory-operations.node
npm notice 17.1kB dist/native/prebuilds/darwin-x64/connect-authority-broker
npm notice 24.3kB dist/native/prebuilds/darwin-x64/directory-operations.node
npm notice 28.9kB dist/native/prebuilds/linux-arm64/directory-operations.node
npm notice 17.4kB dist/native/prebuilds/linux-x64/directory-operations.node
npm notice 1.2kB dist/native/README.md
npm notice 3.1kB dist/orchestrator/format.js
npm notice 13.4kB dist/orchestrator/index.js
npm notice 307B dist/orchestrator/manifest.json
npm notice 117.7kB dist/orchestrator/orchestrator.mjs
npm notice 408B dist/orchestrator/types.js
npm notice 237B dist/skill/propr/agents/openai.yaml
npm notice 6.1kB dist/skill/propr/SKILL.md
npm notice 4.5kB dist/tui/AgentTableApp.js
npm notice 4.5kB dist/tui/app.js
npm notice 11.6kB dist/tui/CheckApp.js
npm notice 5.1kB dist/tui/render.js
npm notice 29.9kB dist/tui/SetupApp.js
npm notice 12.6kB dist/tui/SetupApp.test.js
npm notice 8.2kB dist/tui/StartApp.js
npm notice 3.6kB dist/utils/apiErrorPresentation.js
npm notice 12.0kB dist/utils/directoryDescriptor.js
npm notice 739B dist/utils/dockerPort.js
npm notice 5.0kB dist/utils/envFile.js
npm notice 520B dist/utils/index.js
npm notice 5.7kB dist/utils/io.js
npm notice 1.5kB dist/utils/nativeArtifact.js
npm notice 544B dist/utils/parseState.js
npm notice 417B dist/utils/positiveInteger.js
npm notice 4.5kB dist/utils/privateFilesystem.js
npm notice 4.2kB dist/utils/resolveProject.js
npm notice 7.2kB dist/vendor/local-setup/agents.js
npm notice 62.7kB dist/vendor/local-setup/engine.js
npm notice 5.0kB dist/vendor/local-setup/envFile.js
npm notice 9.1kB dist/vendor/local-setup/github.js
npm notice 188B dist/vendor/local-setup/index.js
npm notice 25.1kB dist/vendor/local-setup/publicInstanceIdentity.js
npm notice 14.6kB dist/vendor/local-setup/state.js
npm notice 2.8kB dist/vendor/local-setup/types.js
npm notice 1.0kB dist/vendor/shared/accountStatusTimestamp.js
npm notice 7.6kB dist/vendor/shared/activityEvents.js
npm notice 3.4kB dist/vendor/shared/agentLogin.js
npm notice 5.1kB dist/vendor/shared/apiOrigin.js
npm notice 8.5kB dist/vendor/shared/connectDiscovery.js
npm notice 254B dist/vendor/shared/demoMode.js
npm notice 4.3kB dist/vendor/shared/desktopPairing.js
npm notice 481B dist/vendor/shared/desktopTokenRevocation.js
npm notice 8.9kB dist/vendor/shared/events.js
npm notice 1.6kB dist/vendor/shared/githubAuthMode.js
npm notice 2.0kB dist/vendor/shared/githubEventIntakeMode.js
npm notice 10.7kB dist/vendor/shared/index.js
npm notice 214B dist/vendor/shared/instanceAuthorization.js
npm notice 54B dist/vendor/shared/instanceCatalog.js
npm notice 4.0kB dist/vendor/shared/intakeModePrerequisites.js
npm notice 2.9kB dist/vendor/shared/labelUtils.js
npm notice 18.5kB dist/vendor/shared/modelDefinitions.js
npm notice 3.5kB dist/vendor/shared/notificationLinks.js
npm notice 51.3kB dist/vendor/shared/notifications.js
npm notice 866B dist/vendor/shared/projectSlug.js
npm notice 3.8kB dist/vendor/shared/proprCompatibility.js
npm notice 13.0kB dist/vendor/shared/proprServiceUrls.js
npm notice 2.2kB dist/vendor/shared/publishedVisualPreviews.js
npm notice 2.3kB dist/vendor/shared/reasoningLevels.js
npm notice 9.4kB dist/vendor/shared/reviewContextBudget.js
npm notice 5.3kB dist/vendor/shared/reviewFeedbackIds.js
npm notice 1.7kB dist/vendor/shared/reviewPrompt.js
npm notice 861B dist/vendor/shared/sessionSecret.js
npm notice 603B dist/vendor/shared/statusKeys.js
npm notice 7.5kB dist/vendor/shared/syntheticAgents.js
npm notice 2.2kB dist/vendor/shared/taskIdentifiers.js
npm notice 647B dist/vendor/shared/taskLifecycle.js
npm notice 3.7kB dist/vendor/shared/usageTips.js
npm notice 503B dist/vendor/shared/usageTypes.js
npm notice 1.1kB dist/vendor/shared/userWhitelist.js
npm notice 758B dist/vendor/shared/validateRelayUrl.js
npm notice 2.2kB dist/vendor/shared/validateRoutingUrl.js
npm notice 7.1kB dist/vendor/shared/visualPreviewCapacity.js
npm notice 9.3kB dist/vendor/shared/voice.js
npm notice 222B dist/vendor/shared/workEvidence.js
npm notice 623B package.json
npm notice Tarball Details
npm notice name: propr-cli
npm notice version: 0.8.15
npm notice filename: propr-cli-0.8.15.tgz
npm notice package size: 375.0 kB
npm notice unpacked size: 1.6 MB
npm notice shasum: 169086d41ee6b3af0a44c1970fbf9ef8ea3c3556
npm notice integrity: sha512-XBjQ7MgsRcYNA[...]EJRoZU4t97LDg==
npm notice total files: 151
npm notice
propr-cli-0.8.15.tgz

Dry run only. Re-run with --publish to publish to npm.
✅ propr-cli release package verified
Starting changed-area validation process...
Reusing this job's @propr/shared build from CLI packaging.
Reusing this job's @propr/local-setup build from CLI packaging.

--- Core Service: Lint & Build ---
✅ Core Lint passed
✅ Core Source Build passed

--- UI: Typecheck, Lint & Build ---
✅ Client Package validation passed
✅ UI Typecheck passed
❌ UI Lint FAILED

> propr-ui@0.0.1 lint
> eslint . --max-warnings 0


/home/runner/work/propr/propr/propr-ui/src/components/Dashboard/CompletedFeed.tsx
  131:96  warning  Arrow function has a complexity of 37. Maximum allowed is 20  complexity

✖ 1 problem (0 errors, 1 warning)

ESLint found too many warnings (maximum: 0).
npm error Lifecycle script `lint` failed with error:
npm error code 1
npm error path /home/runner/work/propr/propr/propr-ui
npm error workspace propr-ui@0.0.1
npm error location /home/runner/work/propr/propr/propr-ui
npm error command failed
npm error command sh -c eslint . --max-warnings 0

--- Docs: Typecheck & Build ---
✅ Docs Typecheck passed
✅ Docs Build passed

--- Core Package: Build & Lint ---
✅ Core Package Build passed
✅ Core Package Lint passed

--- CLI: Lint ---
✅ CLI Lint passed

--- API: Build & Lint ---
✅ API Build passed
❌ API Lint FAILED

> @propr/api@0.8.15 lint
> eslint . --max-warnings 0


/home/runner/work/propr/propr/packages/api/routes/dashboardOutcomeQueries.ts
  456:70  error    '_count' is defined but never used                                                     @typescript-eslint/no-unused-vars
  519:1   warning  File has too many lines (470). Maximum allowed is 400                                  max-lines
  555:8   warning  Async function 'loadOutcomeHistory' has too many parameters (5). Maximum allowed is 4  max-params

/home/runner/work/propr/propr/packages/api/routes/dashboardRoutes.ts
  333:3  warning  Async function 'getOutcomes' has a complexity of 25. Maximum allowed is 20  complexity

/home/runner/work/propr/propr/packages/api/services/dashboardReadService.ts
  185:11  warning  Blocks are nested too deeply (5). Maximum allowed is 4  max-depth
  193:11  warning  Blocks are nested too deeply (5). Maximum allowed is 4  max-depth

✖ 6 problems (1 error, 5 warnings)

npm error Lifecycle script `lint` failed with error:
npm error code 1
npm error path /home/runner/work/propr/propr/packages/api
npm error workspace @propr/api@0.8.15
npm error location /home/runner/work/propr/propr/packages/api
npm error command failed
npm error command sh -c eslint . --max-warnings 0

--- Stage outcomes ---
Workflow lint: success
Release shell lint: success
Pre-setup gate: skipped
Node setup: success
Toolchain: success
Dependency install: success
Release metadata: success
CLI packaging: success
Change detection: success
Changed-area checks: failure

View Workflow

@propr-dev

propr-dev Bot commented Sep 29, 2026 •

Copy link
Copy Markdown
Contributor Author

✅ Applied the requested follow-up changes in commit c85da4f

Processed 2 comments:

  • Comment 1 by @propr-dev[bot] (ID: 5891265817✓)
  • Comment 2 by @github-actions[bot] (ID: 5891266558✓)

Summary of Changes

Fixed all reported lint failures in the four flagged files while preserving behavior and cursor validation.

Validated:

  • UI/API lint with zero warnings
  • Typechecks and builds
  • 62 API tests and 36 UI tests
  • Projection parity/recovery and browser history checks

No visible changes; no previews needed.


🤖 Implementation Details

  • Model: GPT-6 Astra (high)
  • Requested By: @propr-dev[bot], @github-actions[bot]
  • Time: 7m 20s
  • Tokens: 1,416,163 (1,406,068 in / 10,095 out)
  • Cost: $2.57

Undo Changes • View Task Execution


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

🤖 Generated by ProPR
Processing comment IDs: 5891265817✓, 5891266558✓

…while preserving behavior and cursor validation.

Fixed all reported lint failures in the four flagged files while preserving behavior and cursor validation.

Validated:
- UI/API lint with zero warnings
- Typechecks and builds
- 62 API tests and 36 UI tests
- Projection parity/recovery and browser history checks

No visible changes; no previews needed.

PR: #2630
Comment by: @propr-dev[bot] (ID: 5891265817)
Comment by: @github-actions[bot] (ID: 5891266558)
Model: gpt-6-astra
@integry

integry commented Sep 29, 2026

Copy link
Copy Markdown
Owner

/ultrafix

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

propr-dev Bot commented Sep 29, 2026

Copy link
Copy Markdown
Contributor Author

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

First action: /review

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

@propr-dev

propr-dev Bot commented Sep 29, 2026 •

Copy link
Copy Markdown
Contributor Author

✅ AI Code Review Complete requested by @integry

Posted 1 review:

View Task Details

@propr-dev

propr-dev Bot commented Sep 29, 2026

Copy link
Copy Markdown
Contributor Author

🔍 AI Code Review — codex:gpt-6-astra

Overall Evaluation

The PR implements durable outcome summaries and on-demand history with a thoughtful recovery design. It needs changes before merge because two changed paths can leave outcomes permanently unavailable or stale.

✅ Bounded serving reads — Summary reads use the entity projection; history reads use indexed, limited keyset queries without reconstructing source history.

✅ Snapshot and revision protection — History pages validate repository, entity revision, and cursor boundaries within one transaction.

✅ Durable invalidation — Projection changes and outbox entries commit together; publication acknowledgements match the outbox token.

The supplied current-head checks show 30 passed and no failures. This review is static; no commands were run.

Merge blockers

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

F1: 🔴 History reassignment can stall projection processing

  • Required behavior: Captured source changes must reconcile successfully without permanently blocking subsequent projection work.

  • Evidence: packages/api/routes/dashboardOutcomeQueries.ts, advanceOutcomeProjection; packages/api/services/dashboardReadService.ts, installOutcomeProjection.

    1. Existing tasks a and z have been projected, with completion history row H belonging to z.
    2. Updating H.task_id from z to a queues both tasks through the new UPDATE trigger. With an initially empty dirty queue, both receive the same second-resolution age, so a sorts first.
    3. Projecting a attempts to insert completion H. Replacement deletes only runs currently assigned to a, leaving the projected H assigned to z.
    4. The insert violates the runs table’s completion_id primary key. The transaction rolls back, preserving both dirty entries.
    5. Every retry selects a again. Processing z, which would release the conflicting row, never occurs; subsequent outcome changes also remain queued.

    The token and epoch checks protect against stale snapshots but do not resolve ownership conflicts between two dirty tasks. This follows directly from the explicit task-ID update capture, queue ordering, task-local deletion, and global completion-ID uniqueness. static trace: supplied trigger, replacement transaction, and worker retry loop; not executed.

  • Minimum fix: Handle transferred completion IDs during reconciliation: remove or transfer conflicting projected rows and reconcile their former entities in the same fenced transaction, allowing both queued tasks to complete.

F2: 🔴 Fallback service exposes an unusable summary loader

  • Required behavior: The service’s retained supplied-connection fallback must continue serving outcomes instead of selecting a projection that it never initializes.

  • Evidence: packages/api/services/dashboardReadService.ts, startDashboardReadService; packages/api/routes/dashboardRoutes.ts, getOutcomes; packages/api/routes/dashboardNarrative.ts, collectNarrativeFacts.

    1. Start the service with a fresh in-memory database containing completed tasks—the supplied unchanged test explicitly exercises this supported connection path.
    2. The fallback branch returns a .summary loader calling loadOutcomeSummaries, but starts no projection worker and performs no projection installation or advancement.
    3. Pass that service loader to the dashboard routes. The new UI requests summaries by default, so the route selects .summary.
    4. requireOutcomeProjection returns OUTCOMES_NOT_READY on every request. Narrative collection also selects .summary, causing the route to return a null narrative.

    Waiting or retrying cannot recover because this branch has no projection producer. The existing in-memory test checks only service.load, so its success does not cover the newly selected path. static trace: supplied fallback branch, route selection, readiness guard, and narrative error handling; not executed.

  • Minimum fix: Return a functioning legacy-backed .summary loader for connections without projection maintenance, using the already-supported embedded-history response, or initialize and maintain the projection for those connections.

Suggestions

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

S1: 🟢 Automate the new regression harnesses

The parity and browser harnesses cover useful recovery and pagination scenarios, but their documented entry points are standalone script flags. Wiring these into routine automated validation would protect the new behavior against later regressions. This is optional; the supplied passing checks do not establish whether those specific harnesses ran.

S2: 🟢 Complete staging performance measurements

The documentation appropriately distinguishes synthetic reader timings from authenticated dashboard latency. Complete the documented staging measurements, including visibility lag under concurrent writes, to confirm the user-facing improvement. This is deployment evidence, not an additional code blocker.

Score

The indexed reads, cursor validation, and durable outbox are strong, and current checks pass. The projection deadlock and permanently unavailable fallback path require localized corrections before merge.

Score: 6/10


🤖 Review Details

  • Model: GPT-6 Astra
  • Time: 1m 8s
  • Tokens: 73,031 (70,944 in / 2,087 out)
  • Cost: $0.71

View Task


💡 Next step: Comment /fix to address every F# merge blocker, or name records explicitly, as in /fix F3 S5.
F# and S# IDs increment across review comments and remain permanent, so selectors such as /fix F3 F5 stay unambiguous across cycles. S# suggestions stay optional: they are implemented only when you name them, and they never relax a merge blocker.

🤖 Review by ProPR

@propr-dev

propr-dev Bot commented Sep 29, 2026 •

Copy link
Copy Markdown
Contributor Author

✅ Applied the requested follow-up changes in commit cbd8fc1

Addressed 1 AI review comment (IDs: 5894451226)

Addressed findings F1, F2

Summary of Changes

Implemented F1 and F2 only.

  • Completion transfers now reconcile ownership and affected entities within the fenced transaction, preserving former tasks’ queued work.
  • Connections without projection maintenance now serve legacy-backed summaries and narrative data with embedded history.
  • Added nine regressions covering transfers, stale snapshots, rebuilds, and fallback paths.

Validation: 46 tests passed; targeted lint, API typecheck, and diff checks passed. No visual previews created for these backend changes.


🤖 Implementation Details

  • Model: GPT-6 Astra (high)
  • Requested By: @propr-ultrafix
  • Time: 5m 3s
  • Tokens: 776,816 (770,429 in / 6,387 out)
  • Cost: $1.59

View Task Execution


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

🤖 Generated by ProPR

Implemented F1 and F2 only.

- Completion transfers now reconcile ownership and affected entities within the fenced transaction, preserving former tasks’ queued work.
- Connections without projection maintenance now serve legacy-backed summaries and narrative data with embedded history.
- Added nine regressions covering transfers, stale snapshots, rebuilds, and fallback paths.

Validation: 46 tests passed; targeted lint, API typecheck, and diff checks passed. No visual previews created for these backend changes.

PR: #2630
Comment by: @propr-ultrafix (ID: 0)
Model: gpt-6-astra
@propr-dev

propr-dev Bot commented Sep 29, 2026 •

Copy link
Copy Markdown
Contributor Author

✅ AI Code Review Complete requested by @propr-ultrafix

Posted 1 review:

View Task Details

@propr-dev

propr-dev Bot commented Sep 29, 2026

Copy link
Copy Markdown
Contributor Author

🔍 AI Code Review — codex:gpt-6-astra

Overall Evaluation

Ready to merge within the reviewed scope. The PR replaces repeated completion reconstruction with a durable projection and loads earlier updates on expansion. Review was static; no commands were run.

✅ Fenced projection updates — Source triggers capture mutations transactionally, and projection commits check both dirty tokens and rebuild epochs before replacing derived data.

✅ Consistent history pagination — History reads use snapshots, validate repository/entity references, and bind cursors to revisions and ordering boundaries.

✅ Compatibility and recovery — Legacy responses remain supported, while the UI handles pagination, revision changes, retries, and authenticated-scope changes.

The authoritative current-head status reports 30 passed checks, no failures or pending checks, and 14 neutral/skipped checks.

Merge blockers

No merge blockers.

Suggestions

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

S3: 🟢 Complete staging performance validation

Follow the staging measurement plan in packages/api/PERFORMANCE.md: collect authenticated request timings, cold behavior, and completion-to-visibility lag under normal load. The synthetic results and serving-query structure support the approach, but do not establish deployed latency or writer contention. This is a rollout follow-up rather than a demonstrated code defect.

S4: 🟢 Preserve harness coverage in automation

Consider integrating the projection and browser self-tests from scripts/benchmark-dashboard-outcomes.ts into routine automated testing, if they are not already invoked. They exercise valuable recovery and pagination paths beyond the added unit tests. This is optional coverage maintenance; the supplied context does not establish their current CI integration.

Score

The implementation addresses the objective with careful consistency, compatibility, and recovery handling. No actionable defect was established, and current-head checks are green; production performance remains to be measured as documented.

Score: 8/10


🤖 Review Details

  • Model: GPT-6 Astra
  • Time: 43s
  • Tokens: 73,487 (72,300 in / 1,187 out)
  • Cost: $0.68

View Task


💡 Next step: Comment /fix to address every F# merge blocker, or name records explicitly, as in /fix F3 S5.
F# and S# IDs increment across review comments and remain permanent, so selectors such as /fix F3 F5 stay unambiguous across cycles. S# suggestions stay optional: they are implemented only when you name them, and they never relax a merge blocker.

🤖 Review by ProPR

@propr-dev propr-dev Bot removed the ultrafix label Sep 29, 2026
@integry

integry commented Sep 29, 2026

Copy link
Copy Markdown
Owner

/review fable

@propr-dev

propr-dev Bot commented Sep 29, 2026 •

Copy link
Copy Markdown
Contributor Author

✅ AI Code Review Complete requested by @integry

Posted 1 review:

View Task Details

@propr-dev

propr-dev Bot commented Sep 29, 2026

Copy link
Copy Markdown
Contributor Author

🔍 AI Code Review — fable

Overall Evaluation

This PR replaces per-request reconstruction of completed outcomes with a durable, trigger-captured SQLite projection (dashboard_outcome_v1_*), a background projection worker with a dirty-token fence and rebuild epoch, a compact summary contract (entityId/revision, no embedded earlierUpdates), a cursor-paginated history contract, an outbox-driven live wake-up, and an expansion-only history loader in the UI. The engineering is careful: ownership transfer of history rows between tasks, reused history IDs, concurrent source writes during the read snapshot, and rebuild-during-processing are all handled and tested against the legacy reader as an oracle. The work is close to mergeable, but the default deployment path leaves the shipped UI without a completed feed (and without a narrative) for the whole initial backfill, which needs a localized fix before merge.

✅ Fenced write transaction is sound — advanceOutcomeProjection takes the writer lock first (UPDATE state), then re-checks epoch and the dirty token before replacing runs; the two "snapshot invalidated by transfer/rebuild" tests in dashboardOutcomeRoutes.test.ts verify that stale work is discarded and re-queued rather than committed.

✅ History cursors are bound to the entity revision and validated against real rows — loadOutcomeHistory checks v/entity/repository/revision, verifies the cursor names an actual run strictly before the entity's latest completion, and 409s on revision drift; the UI's useOutcomeHistory drops cached pages and reloads the summary on OutcomeHistoryStaleError.

✅ Client keeps backward compatibility — CompletedRow renders item.earlierUpdates when no entityId is present, so legacy servers (and the in-memory summary: load path) continue to work with the new UI.

Merge blockers

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

F3: 🔴 Default deploy serves 503/blank feed and narrative for the entire backfill

  • Required behavior: Changed behavior must not regress the existing dashboard: the completed feed and narrative worked before this PR; after it, a default deployment (no DASHBOARD_OUTCOME_PROJECTION set) makes both unavailable until the backfill of every task finishes, with no client or server fallback.

  • Evidence: packages/api/routes/dashboardRoutes.ts (getOutcomes, summary branch), packages/api/routes/dashboardOutcomeQueries.ts (requireOutcomeProjection), propr-ui/src/api/dashboardApi.ts (getDashboardOutcomes default view = 'summary'), packages/api/services/dashboardReadService.ts (startDashboardReadService starts the projection worker whenever the env var is not legacy).

    Static trace:

    1. Production starts via server.ts:569 startDashboardReadService(db) with no options and the env var unset; the worker installs the catalog and begins seeding 100 tasks per batch, then projects one dirty task per iteration, each iteration also running the full legacy oracle for that task (!generation.ready branch). On the staging data set (14,436 tasks) this is a multi-minute window at minimum.
    2. The shipped UI's CompletedFeed calls getDashboardOutcomes(repository, 50, search) which now defaults to view: 'summary' and sends Accept: application/vnd.propr.outcome-summaries+json.
    3. getOutcomes computes summary = true (env not legacy/shadow) and calls loadOutcomeSummaries → requireOutcomeProjection throws OutcomeProjectionError(503, 'OUTCOMES_NOT_READY') → response 503 { error: 'OUTCOMES_NOT_READY' }.
    4. useDashboardSection records the error; the completed section shows a failure state (the raw code as message) and keeps re-failing on every refresh/poll until ready flips. No caller ever passes view: 'legacy', and getDashboardOutcomes has no fallback on 503.
    5. collectNarrativeFacts in packages/api/routes/dashboardNarrative.ts now prefers options.completedRows.summary, which rejects with the same error; getNarrative swallows it and returns summary: null, so the narrative bar is also blank for the whole window.
    6. If any single task ever fails the per-task parity check (the worker retries the same first-in-queue task every 500 ms and never advances), ready is never set and the outage is indefinite until an operator sets legacy.

    The documented mitigation (deploy first with DASHBOARD_OUTCOME_PROJECTION=shadow) is an out-of-band manual precondition; the code's default is the unsafe path, and the same window recurs after every --rebuild. The server already proves the client accepts legacy rows on a summary request (the in-memory branch returns summary: load with embedded earlierUpdates, and CompletedRow renders them), so a fallback is contract-compatible. Verification: the self-test in scripts/benchmark-dashboard-outcomes.ts asserts loadOutcomeSummaries rejects with OUTCOMES_NOT_READY during backfill (confirms server behavior); the absence of any UI fallback is a static trace of dashboardApi.ts/CompletedFeed.tsx. proposed regression: route test — file-backed DB, projection installed but not ready, request with the summaries Accept header, assert 200 with legacy rows (or assert the UI recovers via view=legacy on 503).

  • Minimum fix: In getOutcomes (and the narrative loader selection), when the projection is not ready, serve the legacy completedRows result to summary-opted clients instead of 503 (e.g., catch OutcomeProjectionError with code OUTCOMES_NOT_READY and fall back to completedRows(repository, { limit, search })); alternatively, make getDashboardOutcomes retry once with view=legacy on a 503 OUTCOMES_NOT_READY. Either is a localized change and keeps the rest of the design intact.

Suggestions

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

S5: 🟢 Projection wake-up event fans out to every completion-interested section

The outbox publisher in dashboardRoutes.ts emits domain: 'task', change: 'completed'. Per useSectionRefreshTokens.ts, that shape wakes attention, stats, summary and completed — so each real completion now costs two rounds of those reads (the original task event, then the projection event ~0.5–1 s later). Given #2611's duplicate-request goal, consider a distinct change/domain (or an entityId the other sections ignore) so only the completed feed and narrative reconcile on projection commit. Optional because correctness is unaffected.

S6: 🟢 A single parity mismatch stalls the whole projection indefinitely

advanceOutcomeProjection always picks the oldest dirty task; on a parity throw the worker sleeps 500 ms and retries the same task forever, re-running the expensive oracle each time and never reaching ready. One divergence between mapTaskRow's title derivation and the SQL resolved_title (e.g., JS trim() vs SQLite trim() on non-space whitespace — not verified) would do this. Consider recording the failing task, marking it as a parity exception, and continuing, or at least backing off the retry. Optional since no concrete mismatch was demonstrated.

S7: 🟢 Source table recreation by future SQLite migrations silently drops capture

Knex SQLite alterTable rebuilds tables (drop/rename), which drops the dashboard_outcome_v1_* triggers. The worker only calls installOutcomeProjection once per process (installed flag), so writes between such a migration and the next restart would be lost from the projection with no error and no automatic rebuild. A lightweight guard (re-check trigger existence periodically or on each advance failure, and mark the projection unready if missing) would make this detectable. Not a current-code defect.

S8: 🟢 Backfill write-lock cadence against the synchronous API connection

During backfill the worker runs a tight loop of write transactions (more ? 0 : 250 ms). The API's better-sqlite3 connection uses a synchronous 30 s busy handler (per PERFORMANCE.md), so each contended API write stalls the event loop for the duration of a worker write tx. Individual transactions are short, but it would be worth measuring on the staging snapshot and adding a small inter-batch yield during initial backfill if stalls are observable.

S9: 🟢 Module-global publisher and hard-coded channel

setOutcomeActivityPublisher is process-global and overwritten by every createDashboardRoutes call; and the channel is the literal 'propr:events:activity' rather than REDIS_CHANNELS.ACTIVITY from packages/shared/src/events.ts. Passing the publisher through startDashboardReadService (or the deps) and using the shared constant would tighten the seam. Cosmetic.

S10: 🟢 Route tests do not exercise the Accept-header opt-in or Vary/Cache-Control

The test harness request has no headers and the response has no getHeader, so the Accept: application/vnd.propr.outcome-summaries+json branch and the Vary merge are only covered by the browser harness (not run in CI). A small route-level test with a headers.accept request and a recording getHeader would close that gap.

Score

The projection, fencing, cursor and UI expansion work is thorough and well-tested, but the default deployment path regresses the dashboard (503 feed, blank narrative) for the whole backfill window with no fallback, which must be corrected before merge.

Score: 6/10


🤖 Review Details

  • Model: Claude Fable 5.1
  • Time: 7m 31s
  • Tokens: 133,870 (99,888 in / 33,982 out)
  • Cost: $2.95

View Task


💡 Next step: Comment /fix to address every F# merge blocker, or name records explicitly, as in /fix F3 S5.
F# and S# IDs increment across review comments and remain permanent, so selectors such as /fix F3 F5 stay unambiguous across cycles. S# suggestions stay optional: they are implemented only when you name them, and they never relax a merge blocker.

🤖 Review by ProPR

@integry

integry commented Sep 30, 2026

Copy link
Copy Markdown
Owner

/fix

@propr-dev

propr-dev Bot commented Sep 30, 2026 •

Copy link
Copy Markdown
Contributor Author

✅ Applied the requested follow-up changes in commit 06e59c9

Addressed 1 AI review comment (IDs: 5898205580)

Addressed finding F3

Summary of Changes

Addressed F3 only. Feed and narrative now fall back to legacy reads on OUTCOMES_NOT_READY, preserving availability during backfill and rebuilds. Ready projections still serve compact summaries; unrelated errors remain visible.

Validation: 49 tests passed, API typecheck passed, UI build passed. Captured a browser preview showing the restored narrative, feed, and expanded history.

Visual preview

Dashboard during projection backfill

Dashboard during projection backfill

Actual feed and narrative route responses from a file-backed database whose projection is not ready, with embedded earlier updates expanded. Synthetic task data and narrative model; existing browser fixture supplies the surrounding dashboard.

Original unavailable: managed storage is unavailable or the upload has expired.


🤖 Implementation Details

  • Model: GPT-6 Astra (high)
  • Requested By: @integry
  • Time: 5m 40s
  • Tokens: 763,091 (756,370 in / 6,721 out)
  • Cost: $1.62

Undo Changes • View Task Execution


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

🤖 Generated by ProPR
Processing comment ID: 5905789588✓

…cy reads on `OUTCOMES_NOT_READY`, preserving availability during backfill and rebuilds. Ready projections still serve compact summaries; unrelated errors remain visible.

Addressed F3 only. Feed and narrative now fall back to legacy reads on `OUTCOMES_NOT_READY`, preserving availability during backfill and rebuilds. Ready projections still serve compact summaries; unrelated errors remain visible.

Validation: 49 tests passed, API typecheck passed, UI build passed. Captured a browser preview showing the restored narrative, feed, and expanded history.

PR: #2630
Comment by: @integry (ID: 5905789588)
Model: gpt-6-astra
@integry

integry commented Sep 30, 2026

Copy link
Copy Markdown
Owner

/review fable

@propr-dev

propr-dev Bot commented Sep 30, 2026 •

Copy link
Copy Markdown
Contributor Author

✅ AI Code Review Complete requested by @integry

Posted 1 review:

View Task Details

@propr-dev

propr-dev Bot commented Sep 30, 2026

Copy link
Copy Markdown
Contributor Author

🔍 AI Code Review — fable

Overall Evaluation

This PR implements issue #2621: it replaces per-request reconstruction of completed outcomes with a durable, trigger-captured SQLite projection (dashboard_outcome_v1_*), serves the feed and narrative from a small indexed read, and moves earlier updates behind an expansion-only, cursor-paginated history endpoint with revision fencing. The legacy reader is kept as the backfill parity oracle and as the fallback for non-opted-in clients and for the not-ready/shadow/legacy states. The UI opts in via an Accept media type, keeps per-row history caches keyed by scope/repository/entity/revision, and recovers from 409 stale responses by reloading the summary.

I traced the projection state machine (seed → dirty → snapshot → fenced write → readiness → outbox), the summary/history/status route branches, the narrative fallback, and the UI hook lifecycle. I did not find a reachable, materially incorrect outcome introduced by the diff. The implementation is careful about the hard cases: token fences against concurrent source writes, epoch fences against rebuilds, ownership transfer of task_history rows between tasks, cursor binding to entity/repository/revision, and explicit 503 rather than a partial feed.

✅ Fenced, idempotent projection writes — advanceOutcomeProjection in packages/api/routes/dashboardOutcomeQueries.ts does the expensive ranking in a read snapshot, then re-checks both the rebuild epoch and the dirty token under the writer lock before replacing a task's runs; stolen completions from other tasks are included in the refresh and the former tasks stay queued. The new tests in dashboardOutcomeRoutes.test.ts exercise transfer, reuse of a history ID, and snapshot invalidation by a concurrent transfer or rebuild against the legacy oracle.

✅ Fail-closed serving with backward compatibility — requireOutcomeProjection throws OUTCOMES_NOT_READY; both getOutcomes and collectNarrativeFacts fall back to the legacy reader only for that code and rethrow everything else (covered by dashboardReadService.test.ts). Legacy clients keep embedded earlierUpdates; the UI handles both shapes (item.entityId ? history.items : item.earlierUpdates).

✅ Cursor integrity — loadOutcomeHistory validates schema version, entity, repository and revision in the cursor and additionally requires the cursor to name an actual run strictly before the entity boundary, so a foreign or fabricated cursor cannot page another entity's history.

Ready to merge within scope. The suggestions below are hardening and follow-ups, not blockers.

Merge blockers

No merge blockers.

Suggestions

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

S11: 🟢 A single failing task stalls the whole projection queue

In packages/api/routes/dashboardOutcomeQueries.ts the dirty queue is drained strictly in (changed_at, task_id) order, and runOutcomeProjectionWorker (packages/api/services/dashboardReadService.ts) catches any per-task failure, logs console.error, increments failures, sleeps 500 ms and retries the same head-of-queue task forever. A parity mismatch (or any deterministic per-task failure such as a NULL sort_at) therefore blocks all later tasks, prevents readiness indefinitely, and emits two error lines per second. Fail-closed is the documented intent, but consider quarantining the failing task (move it to the tail or record it separately), exponential backoff, and rate-limited logging so one bad row does not freeze incremental catch-up for every other entity.

S12: 🟢 Outbox wake-up frame double-wakes every dashboard section

createDashboardRoutes publishes the projection wake-up as domain: 'task', change: 'completed'. Per useSectionRefreshTokens.ts SECTION_INTERESTS, that shape is relevant to attention, active, stats, summary and completed, so each real completion now produces two refresh rounds for all five sections (the original lifecycle event and the outbox event ~0.25–1 s later, likely outside any coalescing window). It is functionally correct, and the second wake is actually what makes the projected feed converge, but a dedicated change type (e.g. change: 'projected' with an interest declared only by completed) would keep the request reduction goal of #2611/#2621 intact. Also worth confirming that no other Redis subscriber on propr:events:activity treats synthetic task/completed frames as real completions.

S13: 🟢 Route-level history behaviour is only asserted in the benchmark script

--self-test in scripts/benchmark-dashboard-outcomes.ts is where the 404 repository mismatch, 400 limit cap, INVALID_HISTORY_CURSOR, HISTORY_STALE on a stale cursor, REPOSITORY_REQUIRED, full-page pagination parity, and the outbox/acknowledgement path are checked. CI runs the node:test suites, not this script, so regressions in getOutcomeHistory or loadOutcomeHistory would not fail a check. Porting those assertions into dashboardOutcomeRoutes.test.ts would make the coverage durable.

S14: 🟢 Worker bootstrap diverges from the existing worker path

The projection worker is started from a data: URL module that re-imports import.meta.url (with a tsx registration branch), whereas the #2611 read worker uses workerUrl(). This works in dev and compiled CI, but packaged/asar desktop builds were not exercised here (Full Test Suite Native Electron skipped), and a failing bootstrap restarts every second while installOutcomeProjection failures retry every 500 ms with logging. Reusing workerUrl() (or covering the packaged path) would reduce deployment risk.

S15: 🟢 Background writer contention with the API's synchronous busy handler

The projection worker performs one write transaction per task with no pause between iterations during backfill (more ? 0 : 250). PERFORMANCE.md notes the API connection uses a synchronous 30 s busy_timeout, so any foreground write that collides with a projection write blocks the API event loop for the duration of that transaction. Individual transactions are short, but consider a small inter-task delay during backfill or measuring foreground write latency on staging while the backfill runs, given that off-thread responsiveness was the point of #2611.

S16: 🟢 Verify apiFetch header merging

readJson in propr-ui/src/api/dashboardApi.ts now passes headers: { Accept: ... } on every dashboard read. If apiFetch replaces rather than merges default headers (e.g. a Desktop bearer token), all dashboard reads would lose them. The supplied context does not include apiFetch, and current checks pass, so this is only a request to confirm the merge behaviour.

S17: 🟢 Centralize the projection mode check

['legacy', 'shadow'].includes(process.env.DASHBOARD_OUTCOME_PROJECTION ?? '') appears in dashboardRoutes.ts and dashboardNarrative.ts, and === 'legacy' in dashboardReadService.ts and requireOutcomeProjection. A single projectionMode() helper would prevent the modes drifting apart.

S18: 🟢 Document trigger runtime coupling

The capture triggers reference unixepoch() (SQLite ≥ 3.38) and the dashboard_outcome_v1_dirty table. Any process opening the database with an older SQLite build, or an operator dropping the private tables during a rollback, would make every tasks/task_history write fail. The rollback section of PERFORMANCE.md says not to drop source data; it should also say explicitly never to drop the dirty table while triggers exist, or provide a --uninstall that drops triggers first.

Score

The projection design is careful and well-fenced, the fallback paths are explicit, and the new tests cover the hardest reconciliation cases; all current-head checks pass. Remaining items are hardening and operational follow-ups rather than correctness defects.

Score: 8/10


🤖 Review Details

  • Model: Claude Fable 5.1
  • Time: 7m 32s
  • Tokens: 141,933 (107,333 in / 34,600 out)
  • Cost: $3.07

View Task


💡 Next step: Comment /fix to address every F# merge blocker, or name records explicitly, as in /fix F3 S5.
F# and S# IDs increment across review comments and remain permanent, so selectors such as /fix F3 F5 stay unambiguous across cycles. S# suggestions stay optional: they are implemented only when you name them, and they never relax a merge blocker.

🤖 Review by ProPR

@integry
integry merged commit a9e4a28 into main Sep 30, 2026
44 checks passed
@integry
integry deleted the 2621/gpt-6-astra-make-dashboard-outcomes-i-20260929-1156-lmt branch September 30, 2026 14:23
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.

Make dashboard outcomes incremental and load earlier updates on demand

2 participants