## Detection


## detectors.detect()


Runs every detector over one transcript's events.


Usage

``` python
detectors.detect(events)
```


#### Parameters


`events: Sequence[TranscriptEvent]`  
The transcript's full ordered event stream.


#### Returns


`list[FeedbackCandidate]`  
Every feedback candidate the detectors found, in detector order.


## models.FeedbackCandidate


A single piece of developer pushback extracted from a transcript.


Usage

``` python
models.FeedbackCandidate(
    dedup_key,
    source_kind,
    occurred_at,
    text,
    window,
    ref,
    signal,
    session_id=None,
    cc_version=None,
    payload=None
)
```


#### Attributes


`dedup_key: DedupKey`  
The content-derived key that makes ingestion idempotent.

`source_kind: SourceKind`  
Which detector produced the candidate.

`occurred_at: datetime`  
When the feedback was given.

`text: str`  
The verbatim pushback text.

`window: ContextWindow`  
The durable context window around the feedback, captured via `~cc_transcript.context.capture_window()`.

`ref: EventRef`  
The resolvable reference to the originating event.

`signal: CandidateSignal`  
The de-noising confidence signal.

`session_id: SessionId | None`  
The transcript session the feedback came from.

`cc_version: str | None`  
The Claude Code version recorded for the origin.

`payload: Mapping[str, Any] | None`  
Detector-specific metadata preserved verbatim.


## models.dedup_key()


Returns the stable dedup key for `parts`.


Usage

``` python
models.dedup_key(*parts)
```


Detectors key on session, kind, and the feedback content (plus its code location for review comments) rather than the transcript entry's uuid or the absolute file path, so the same pushback recorded under two transcript entries collapses to one row, and the database stays portable and idempotent across moves.


#### Parameters


`parts: str = ()`  
The content fragments that uniquely identify a candidate.


#### Returns


`DedupKey`  
The SHA-256 hex digest of the parts joined by a null byte.
