fix(frontend): reconnect stream on same-session resume

This commit is contained in:
User
2026-07-14 20:53:19 +02:00
parent b1d1284cc8
commit 1393c6358e
5 changed files with 90 additions and 47 deletions
+50 -44
View File
@@ -1,59 +1,65 @@
# Task 3 report — one secret bundle for local services
# Task 3 report — reconnect SSE on same-session Resume
## Status
Complete. Local pgvector bootstrap, reconciliation, migration, and preprocess services now
mount only `/run/secrets/thothii.secrets`. `deploy/vector/secret-policy.sh` validates the
whole bundle (allowlist, duplicate/empty/unknown keys, comments/blank lines, mode and symlink
policy) and returns only the requested value. The core entrypoint exposes DWH/vector/CA values
to the harness and materializes short-lived 0600 password files for workspace resolution.
Complete. A successful Resume of the currently active session now replaces its existing
`EventSource` connection. Resuming a different session continues to reconnect through the
session ID change only, without a generation-driven second connection.
## Implementation
- `useSessionStream` accepts an optional `generation` argument (default `0`) and includes it
in the stream effect dependencies. A generation change therefore runs the existing cleanup,
closes the old source, and opens the same URL again.
- `AppShell` captures whether the requested Resume ID is already active before its existing
optimistic state updates. It increments the stream generation only after `resumeSession(id)`
succeeds and only for that same-ID case.
- The existing optimistic session switch, phase refresh, and failed-Resume rollback remain
unchanged. A failed POST cannot increment the generation.
## TDD evidence
- RED: `./scripts/test-preprocess-compose-config.sh` failed on the pre-existing
`vector_reader_password` Compose secret declaration.
- GREEN: the same command passes after the bundle conversion and verifies local-vector
workspace interpolation and shared secret mounts.
- `./scripts/test-vector-secret-policy.sh` covers comments/blank lines and rejects an
unrelated duplicate key.
- RED command:
`cd frontend && npx vitest run src/stream/useSessionStream.test.tsx src/shell/AppShell.session-mgmt.test.tsx`
- RED result: 2 expected failures and 15 passes. The hook test observed
`first.closed === false`; the AppShell test observed one `FakeEventSource` instead of two
after the second same-ID Resume.
- GREEN focused result: the same command passed 2/2 files and 17/17 tests after the minimal
production wiring.
## Verification
## Full verification
- `./scripts/test-vector-secret-policy.sh` — passed.
- `./scripts/test-preprocess-compose-config.sh` — passed.
- `./scripts/test-vector-backup-restore-safety.sh` — passed.
- `./scripts/test-default-compose.sh` — passed.
- `./scripts/test-container-deployment.sh` — passed.
- `./scripts/local-vector-smoke.sh` — passed with real Docker (bootstrap rotation, role
reconciliation, migration, persistence and restart).
- `./scripts/preprocess-smoke.sh` — passed with real Docker (unchanged rerun, mutation, DWH
job, ACTIVE publication and cleanup).
- `./scripts/preprocess-smoke.sh --cleanup-failure` — passed.
- `git diff --check` and `sh -n` gates — passed.
- Baseline before edits: `cd frontend && npx vitest run` — 42/42 files and 251/251 tests passed.
- Focused tests: 2/2 files and 17/17 tests passed.
- Full frontend suite: `cd frontend && npx vitest run` — 42/42 files and 253/253 tests passed.
- Typecheck: `cd frontend && npx tsc -b` — exit 0.
- Production build: `cd frontend && npm run build` — exit 0; Vite transformed 4,835 modules
and completed the production bundle.
- `git diff --check` — passed.
## Critical review fix
The suite and build retained the pre-existing MSW unhandled-request, React ref/`act`, Node type
stripping, and Vite chunk-size warnings. This task introduced no new warning category.
`buildPiChildEnv` now removes `THT_DWH_API_KEY`, `THT_VEC_API_KEY`, `THT_VEC_WRITE_API_KEY`,
`THT_SSL_CA`, `THT_CA`, and their file metadata before spawning Pi. A regression test proves
that neither secret values nor bundle/file metadata are inherited by the Pi child.
## Files
## Commits
- `frontend/src/stream/useSessionStream.ts`
- `frontend/src/stream/useSessionStream.test.tsx`
- `frontend/src/shell/AppShell.tsx`
- `frontend/src/shell/AppShell.session-mgmt.test.tsx`
- `.superpowers/sdd/task-3-report.md`
- `70a19f2 feat(compose): use one secret bundle for local services`
- `d500563 fix(security): scrub deployment secrets from Pi child`
- `8518a73 fix(security): scrub raw deployment secret values`
## Self-review
## Concern
- Confirmed the old EventSource is closed before the replacement is retained by React's effect
lifecycle, and the replacement uses the identical session URL.
- Confirmed same-ID detection happens before the optimistic `setActiveSessionId(id)` call.
- Confirmed the generation increments only after a successful Resume POST; the catch/rollback
branch is unchanged.
- Confirmed a different ID leaves the generation unchanged, so the existing session-ID effect
change creates exactly one replacement connection.
- Confirmed the diff is frontend-only apart from this report and contains no backend, Docker,
configuration, or session changes.
The rotation helper retains its old/new scratch-file CLI contract; smoke tests keep those files
outside Compose and mount only the bundle.
## Concerns
## Whole-branch review fixes
- `core-entrypoint.sh` validates `THT_SECRETS_FILE` fail-closed before optional lookups; malformed,
duplicate, unknown, oversized, or overlong bundles stop startup with sanitized diagnostics.
- Runtime password files are cleaned after child exit via signal forwarding and `wait`, rather
than being orphaned by `exec`.
- The shell loader accepts CRLF bundles (Windows/Notepad) consistently with the TypeScript loader.
- Optional key lookup distinguishes an absent key from an invalid value; present malformed
credentials now stop entrypoint startup instead of being silently ignored.
None.