Files
pulse/docs/release-control
rcourtman 533027ee06 Drop Patrol state members no surface reads (#2695)
`usePatrolIntelligenceState()` returned 18 members that no file outside
the hook reads. A TypeScript checker scan of src and tests found no
readers and no spread, computed or `Object.*` escapes of the state
object. Several were also dead inside the hook.

## Change

- **Return object.** Drops activePatrolFindings, activityRefreshTrigger,
acceptedManualRunId, assistantHandoffFindingId, circuitBreakerStatus,
fullModeUnlocked, initialSurfaceReady, isRefreshing, loadAllData,
openAssistantOperationsLoopForFinding, the four decision/outcome counts,
both work-evidence counts, recordPatrolWorkflowStarterActivity and
setFullModeUnlocked, plus handleAssistantFindingHandoff (see below).
- **Dead locals.** Removes the write-only signals (initialSurfaceReady,
isRefreshing, fullModeUnlocked, assistantHandoffFindingId), the 15 s
background refresh timeout that only reset isRefreshing, the unread
memos, and the never-called `openAssistantOperationsLoopForFinding`.
That last one was a second copy of the handoff `FindingsPanel` already
performs. Locals still used inside the hook (activePatrolFindings,
loadAllData, activityRefreshTrigger, acceptedManualRunId) stay.
- **Test-only exports.** `openPatrolAssistantWorkflowHandoff` and
`recordPatrolWorkflowStarterActivity` were used only by that dead path
and by their own tests, so they go.
- **Workspace.** `handleAssistantFindingHandoff` only wrote the unread
handoff signal, so `PatrolIntelligenceWorkspace` no longer passes
`FindingsPanel` an `onAssistantHandoff` callback. The prop is optional.
- **Tests.** Raw-source pins on removed code are dropped. The
manual-sync timeout test now pins the Retry path's own generation guard;
a mutation of its timeout fails it. The non-blocking starter test now
exercises `recordPatrolControlStarterActivity`.
- **Contracts.**
- patrol-intelligence said a finding-to-Assistant handoff *must* record
a `pulse_patrol` workflow starter. Only the dead path did that; the live
`FindingsPanel` handoff never has, so `pulse_patrol` has no browser
producer. The clause now says that and keeps the content-free rules as a
condition.
- The "local Patrol state must expose work evidence" sentence no longer
requires counts nothing reads.
- Three refresh clauses that described a header refresh action now name
the stale-data Retry, the only operator refresh left.
- api-contracts and frontend-primitives record that `FindingsPanel` owns
the handoff.

## Not in this PR

The store still fetches `GET /api/ai/circuit/status` for nothing. That
removal is a follow-up after #2677, because it edits the same lines.

## Evidence

- Production builds before and after compared with chunk hashes, SRI
strings and short identifiers normalised. Only the removed hook code
differs, the unused `pulse_patrol` constant drops out of `aiChat.js`,
and `index.js` reorders the same preload list. `check-bundle-size.mjs`
passes.
- Private demo-mode stacks for both builds, driven by the same
Playwright script at 1440x900 and 390x844: Patrol load, Activity >
Finding options and history, Open in Assistant (drawer opens with the
finding), tab hide/show, and the forced stale-data banner plus Retry.
Each phase hit the same `/api` endpoints on both builds, and neither
build had console errors.
- `tsc --noEmit`, eslint, and vitest for `src/features/patrol`,
`AIIntelligence.test.tsx`, `frontendResourceTypeBoundaries.test.ts` and
`FindingsPanel.test.ts` pass, as does the full pre-commit battery.

Stacked on #2687 (which carries #2682 and #2686). Until those merge,
this PR shows their commits too. `git merge-tree` is clean against main,
#2690, #2677 and #2691.
2026-10-08 03:01:08 +01:00
..