Skip to content

perf: Stop-hook latency dominated by repeated transcript parsing (~16s/stop) #2885

@alonf

Description

@alonf

Summary

The end-of-turn Stop hook is slow (~16s per stop on a dev machine; observed ~1m40s in the Claude UI on a OneDrive/AV machine, which also includes the model's own cogitating). Surfaced during the feature-185 (host-neutral gate enforcement / FR-011 conformance Stop-provider) dogfood on real Claude in a throwaway test-f185 project.

The latency is dominated by repeated transcript parsing, and the largest share is pre-existing (F-174 handover provider) — not introduced by feature 185.

Evidence (measured against the deployed providers + a real ~0.25 MB / 80-line transcript)

Component Time Origin
Handover provider (Stop, order 30) 11.3 s pre-existing (F-174)
Conformance provider (Stop, order 40) 4.2 s feature 185 (FR-011)
Full dispatcher Stop run ~16 s sum

Other factors ruled out by measurement: cold pwsh -NoProfile spawn = 0.53 s; shared-governance.ps1 load = 0.74 s; a single Get-Module -ListAvailable Specrew scan = 0.61 s. So the dominant cost is not spawns or module load — it is transcript parsing: Get-SpecrewConversationTurnFromLine does ConvertFrom-Json -Depth 40 per line, and the readers each re-read + re-parse -Tail 500:

  • Handover calls it ~3x per stop: Get-SpecrewCapturedBoundaryVerdict + Get-SpecrewCapturedBoundaryPacket + the conversation-tail capture (each ~2.9 s on this small transcript).
  • Conformance adds another parse (the last-assistant read + the false-positive guard's packet read).
  • Cost scales with session size, so it degrades as the conversation grows.

Already done (feature 185, commit 9834354b)

The conformance provider was optimized so it no longer compounds the cost unnecessarily:

  • Lazy Get-Module (only scans when the direct path / SPECREW_MODULE_PATH miss).
  • Skips the per-line parse entirely when no trigger is structurally possible (e.g. the pre-spec design workshop).
  • #3 raw-Spec-Kit detection is now a cheap raw-text tail scan (no per-line JSON parse).

This trims feature 185's ~4 s share but leaves the dominant ~11 s handover cost unaddressed.

Proposed fix (the real unblock)

Parse the transcript tail ONCE per stop and share the parsed turns across the handover readers (verdict + packet + conversation tail) and the conformance provider, instead of each re-reading and re-parsing. Secondary: cap the parse window / ConvertFrom-Json -Depth, and/or stop scanning once the needed marker is found (scan from the end backward).

This is primarily an F-174 handover change (the dominant cost), composing with the FR-011 conformance provider.

Acceptance

Repro

  1. Import-Module <worktree>\Specrew.psd1 -Force; specrew init --project-path <tmp> --force (deploys the FR-011 conformance Stop-provider).
  2. Launch Claude in <tmp> and drive a few turns.
  3. Time a Stop: Measure-Command { <dispatcher> -Event Stop -HostKind claude } against the session transcript.

Refs

  • Feature 185 (FR-011 conformance Stop-provider), drift-log D-005/D-006.
  • Conformance perf fix: commit 9834354b.
  • Suggested labels: performance, tech-debt (only enhancement applied — others did not exist).

Metadata

Metadata

Assignees

No one assigned

    Labels

    enhancementNew feature or request

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions