9.6 KiB
spec, created, status
| spec | created | status |
|---|---|---|
| docs/specs/ui/requirements/task-prompt-transcript-visibility.md | 2026-08-22 | implemented |
Implementation plan: Restore complete transcript history
Overview
Fix GitHub issue 2914 and the related older-history scrolling defect. Two independent frontend conditions break the path to the first prompt.
A throwaway regression test confirmed the defect. It supplied one visible
agent message and a task description. The test expected
["task-description", "boot"] but received ["boot"]. The focused test run
reported 1 failure and 51 passes. The throwaway change was then removed.
The task-description fallback currently requires
visibleMessages.length === 0. Any agent or environment row removes it. The
native transcript also calls useLazyLoadSentinel with its one-shot defaults.
When an older page only enlarges a collapsed activity row, the sentinel stays
in view but does not re-arm. The existing message-pagination E2E documents the
manual button as the workaround for this exact state.
The shared sentinel already supports safe positive-result re-arming. Its focused tests cover the enabled and disabled re-arm cases and the zero-result guard, but browser behavior also requires an eligibility retry after a blocked intersection and a deferred follow-up after prepend layout settles. The repair extends that shared behavior without a backend or API change.
Frontend
- Update
apps/web/hooks/use-processed-messages.tsso the synthetic task description depends on the absence of a visible user-authored message. - Keep the synthetic message before agent and environment messages.
- Suppress the synthetic message as soon as visible history contains a real user message. This avoids duplicate prompts.
- Preserve the existing empty-description behavior.
Older-history scrolling
- Update
apps/web/components/task/chat/message-list-native-scroll.tsso the native transcript enablesrearmWhileIntersectingon its shared sentinel. - Keep
joinInFlightWhileLoadingdisabled. The transcript must wait for its current request instead of joining another load from the sentinel callback. - Let the intersection state stop the chain after visible older content pushes the sentinel above its preload region.
- Change
useScrollPositionOnPrependto detect older-page progress from the oldest non-synthetic message. The fixed task-description row must not hide a prepend from the scroll anchor. - Keep the explicit Load older messages button for error and no-progress recovery.
- Preserve the existing prepend scroll anchoring. Do not expand activity rows or change the scroll container.
Tests
- Add
apps/web/hooks/use-processed-messages-fallback.test.tswith a regression test that fails before the fix and passes after it. - Cover agent-only visible history with a task description. Confirm that the synthetic task message remains first.
- Cover visible user history with a task description. Confirm that no synthetic duplicate appears.
- Extend
apps/web/components/task/chat/message-list-native.test.tsxwith a transcript integration case. Confirm positive-result re-arming and stable scroll position with a fixed task-description row. - Keep the shared zero-result and stale-completion cases in
apps/web/hooks/use-lazy-load-sentinel.test.tsgreen.
E2E tests
- Replace the repeated button actions in
apps/web/e2e/tests/chat/message-pagination.spec.tswith upward scrolling. Seed more than 100 same-turn tool events so older pages only extend a collapsed row. Confirm that pagination reaches the first prompt without a button action. Measure the recent standalone row before and after the load so prepend anchoring cannot move the reading position. - Add
apps/web/e2e/tests/chat/mobile-message-pagination.spec.tswith the same user outcome and scroll-position assertion in themobile-chromeproject.
Desktop and mobile use the same native transcript and scroll owner. The mobile entry point remains the task chat, and the existing full-height task layout is the nearest mobile exemplar. This change adds no control, overlay, layout, or touch target. The mobile scenario proves that the phone scroll container also reaches the first prompt without repeated actions.
Backend
No backend change is required. The standard launch path already records the initial prompt. Database repair and message backfill remain outside this fix.
Verification commands
Run the unit regressions during the red and green TDD steps:
cd apps && pnpm --filter @kandev/web test -- \
hooks/use-processed-messages.test.ts \
hooks/use-processed-messages-fallback.test.ts \
hooks/use-lazy-load-sentinel.test.ts \
components/task/chat/message-list-native.test.tsx
Run the focused browser regression after the unit test passes:
cd apps/web && pnpm e2e:run --project chromium \
tests/chat/message-pagination.spec.ts
cd apps/web && pnpm e2e:run --project mobile-chrome \
tests/chat/mobile-message-pagination.spec.ts
Run the web type check after the implementation is complete:
cd apps/web && pnpm run typecheck
Verification results
- Task 01 red:
cd apps && pnpm --filter @kandev/web test -- hooks/use-processed-messages.test.tsfailed at the new agent-only fallback assertion before the production change (1 failed, 51 passed). - Task 01 green: the same focused unit command passed (53 tests).
- Task 02 red: the focused native-scroll command failed the new sentinel option and prepend-anchor assertions before the production change (2 failed, 16 passed).
- Task 02 green:
cd apps && pnpm --filter @kandev/web test -- hooks/use-processed-messages.test.ts hooks/use-processed-messages-fallback.test.ts hooks/use-lazy-load-sentinel.test.ts components/task/chat/message-list-native.test.tsxpassed (4 files, 84 tests). - Web typecheck:
cd apps/web && pnpm run typecheckpassed. - Web lint:
pnpm --filter @kandev/web lintpassed with zero warnings. - Desktop E2E:
cd apps/web && pnpm e2e:run --project chromium tests/chat/message-pagination.spec.tspassed (1 test). - Mobile E2E:
cd apps/web && pnpm e2e:run --project mobile-chrome tests/chat/mobile-message-pagination.spec.tspassed (1 test). - PR fixup unit suite:
cd apps && pnpm --filter @kandev/web test -- hooks/use-processed-messages.test.ts hooks/use-processed-messages-fallback.test.ts hooks/use-lazy-load-sentinel.test.ts components/task/chat/message-list-native.test.tsx components/task/chat/message-list-shared.test.tsx components/task/chat/transcript-auto-scroll.test.tspassed (6 files, 162 tests). - PR fixup: synthetic task-description rows now stay out of prompt navigation, equal-count synthetic-to-stored replacements preserve the scroll anchor, the synthetic row construction stays referentially stable while streaming, and the metadata overflow locators distinguish the stored row from its fallback.
- PR fixup desktop last-prompt suite:
cd apps/web && pnpm e2e:run --project chromium tests/chat/last-prompt-scroll.spec.tspassed (11 tests). - PR fixup desktop metadata suite:
cd apps/web && pnpm e2e:run --project chromium tests/chat/message-metadata-overflow.spec.tspassed (1 test). - PR fixup mobile metadata suite:
cd apps/web && pnpm e2e:run --project mobile-chrome tests/chat/mobile-message-metadata-overflow.spec.tspassed (1 test). - PR fixup web typecheck and lint passed with zero errors and warnings.
- Final PR-fixup remediation: the shared re-arming sentinel now retries when eligibility clears after an observed blocked state and schedules the next positive page after prepend layout settles. Desktop and mobile pagination regressions now continue upward scrolling at each boundary and assert the anchored row after each prepend.
- Final remediation verification: focused unit tests passed (6 files, 163 tests), web typecheck passed, web lint passed with zero errors and warnings, and rebuilt-bundle desktop and mobile pagination E2E each passed (1 test).
- Blocker remediation: sentinel-owned loads are serialized, one deferred continuation is reserved per positive page, and idle transitions from those loads cannot schedule a second continuation. The stateful 20/20/20/0 regression passes with one invocation per cursor, including the final zero result.
- After merging the current
origin/main, the focused unit suite passed again (6 files, 170 tests), web typecheck and lint remained clean, and the backend E2E binary and fixture package were rebuilt before rerunning both pagination E2E specs successfully (1 desktop test, 1 mobile test).
Implementation waves
Execution is sequential in the primary conversation.
Wave 1:
Wave 2:
Wave 3:
Risks
- The fallback must inspect messages after visibility filtering. A hidden user row must not remove the only prompt that the user can see.
- A stored visible user prompt must remain authoritative. Otherwise normal sessions show duplicate prompts.
- The change must not alter message ordering after the fallback decision.
- Automatic re-arm must occur only after positive progress. A zero-result or rejected request must not create a tight request loop.
- Scroll anchoring must preserve the visible reading position after a new standalone row appears above it.
- The explicit load button must remain available as a recovery path.
Public documentation
None. This repair restores expected transcript behavior and does not change a public command, configuration key, API, or workflow.