Mining
mining.MiningSignal
A neutral fact mined from a transcript, ready for an app to map to a candidate.
Usage
mining.MiningSignal(
kind,
detector,
session_id,
event_index,
event_uuid,
occurred_at,
text,
cc_version,
trigger_index,
signal,
lower_bound=None,
evidence=dict()
)Attributes
kind: SourceKind-
The descriptive source category.
detector: str-
The sub-discriminator naming the shape that produced the signal.
session_id: SessionId-
The session the originating event belongs to.
event_index: int-
The index of the originating event in the stream.
event_uuid: EventUuid-
The originating event’s uuid.
occurred_at: datetime-
When the originating event was recorded.
text: str-
The mined feedback text.
cc_version: CcVersion | None-
The Claude Code version recorded for the origin.
trigger_index: int | None-
The nearest preceding assistant index, or None — a hint the app may use; absence never disqualifies the fact here.
signal: CandidateSignal-
The de-noising confidence signal; apps re-derive as needed.
lower_bound: int | None-
A context anchor, such as the plan-reentry edit index.
evidence: Mapping[str, Any]- Detector-specific metadata preserved verbatim.
mining.CandidateSignal
A confidence verdict on a mined fact, with the reasons that produced it.
Usage
mining.CandidateSignal(confidence, reasons=(), durable=True)Attributes
confidence: Confidence-
The de-noising score in [0, 1].
reasons: tuple[str, …]-
The short reason codes that justify the score.
durable: bool- Whether the signal should persist across re-derivation.
mining.Confidence
A de-noising score in the closed interval [0, 1]; higher is more trustworthy.
mining.Confidence=NewType("Confidence", float)
mining.NOISE_FLOOR
mining.NOISE_FLOOR=LOW
mining.MiningSpec
The full declarative mining policy the Rust executor interprets.
Usage
mining.MiningSpec(
detectors=ALL_DETECTORS,
user_message=USER_MESSAGE_SPEC,
calibrated=CALIBRATED_SPEC,
provenance=ProvenanceSpec(),
review=ReviewSpec(),
reentry_lookback=REENTRY_LOOKBACK,
edit_tools=(lambda: expand_tool_names(_TOOL_SETS["EDIT_TOOLS"]))(),
plan_tools=(lambda: expand_tool_names(_TOOL_SETS["PLAN_TOOLS"]))(),
denial_excluded_tools=(lambda: expand_tool_names(_TOOL_SETS["DENIAL_EXCLUDED_TOOLS"]))()
)Attributes
detectors: frozenset[DetectorName]-
The detector ids to run; absent detectors are skipped.
user_message: ConfidenceSpec-
Scoring stages for the transcript-message detector.
calibrated: ConfidenceSpec-
Calibration stages seeded per call by the denial, plan, and review detectors.
provenance: ProvenanceSpec-
The provenance classification policy.
review: ReviewSpec-
The review-comment detector’s format and surface policy.
reentry_lookback: int-
How many events back the plan-reentry detector scans for an edit.
edit_tools: frozenset[str]-
The tool names whose use anchors a plan-reentry edit.
plan_tools: frozenset[str]-
The plan-submission tool names whose denials mine as plan rejections.
denial_excluded_tools: frozenset[str]- The tool names whose denials the denial detector skips.
mining.mine()
Mines every MiningSignal from already-parsed transcript events.
Usage
mining.mine(events, spec)The Rust detector pipeline runs over materialized ~cc_transcript.models.TranscriptEvent objects, so a consumer that already parsed a transcript mines it without re-reading the file, and each mined signal’s event_index addresses straight back into the same events list. Each ~cc_transcript.mining.spec.CallableReviewFormat’s Python pattern and extractor fire through a positional side-channel. The events are materialized eagerly, since the Rust backend indexes them positionally, so a malformed spec pattern raises here rather than mid-stream.
Parameters
events: Sequence[TranscriptEvent]-
The parsed transcript events, in stream order.
spec: MiningSpec- The mining policy: which detectors run, with which scoring, provenance, and review-format policy.
Yields
MiningSignal- Neutral mined facts, one per recognized transcript shape, in detector order.
mining.mining_spec_to_json()
Serializes spec to the JSON contract consumed by the Rust mining executor.
Usage
mining.mining_spec_to_json(spec)mining.DENIAL_PREFIX
mining.DENIAL_PREFIX=literal_str("protocol.DENIAL_PREFIX")
mining.DENIAL_KIND_USER_REJECTED
mining.DENIAL_KIND_USER_REJECTED=literal_str("protocol.DENIAL_KIND_USER_REJECTED")
mining.DENIAL_KIND_PERMISSION_RULE
mining.DENIAL_KIND_PERMISSION_RULE=literal_str("protocol.DENIAL_KIND_PERMISSION_RULE")
mining.USER_SAID_MARKER
mining.USER_SAID_MARKER=literal_str("protocol.USER_SAID_MARKER")
mining.USER_SAID_TRAILER
mining.USER_SAID_TRAILER=literal_str("protocol.USER_SAID_TRAILER")
mining.ANSWERED_PREFIX
mining.ANSWERED_PREFIX=literal_str("protocol.ANSWERED_PREFIX")
mining.ANSWERED_TRAILER
mining.ANSWERED_TRAILER=literal_str("protocol.ANSWERED_TRAILER")
mining.SourceKind
A descriptive category for a mined feedback fact.
mining.SourceKind=NewType("SourceKind", str)
The five module constants are the common categories the fact-detectors emit; apps may define their own SourceKind values for categories the core does not name.
mining.FeedbackCandidate
A single piece of developer pushback extracted from a transcript.
Usage
mining.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.
mining.DedupKey
A content-derived SHA-256 key that makes feedback ingestion idempotent.
mining.DedupKey=NewType("DedupKey", str)
mining.dedup_key()
Returns the stable dedup key for parts.
Usage
mining.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.
mining.ReviewComment
A single inline review comment parsed from a code-review message.
Usage
mining.ReviewComment(file, line_start, line_end, comment)Attributes
file: str | None-
The file the comment targets, when cited.
line_start: int | None-
The first line the comment targets, when cited.
line_end: int | None-
The last line the comment targets, when a range is cited.
comment: str- The comment’s text.
mining.ReviewFormat
mining.ReviewFormat=RegexReviewFormat | CallableReviewFormat
mining.FeedbackStore
Persistent store for collected feedback over the native store engine.
Usage
mining.FeedbackStore(engine, schema)Owns the one connection to feedback.db — the feedback_events ledger, the scanned-file mtime table, and the verdict tier. Recording a scanned file and inserting its candidates commit in one transaction, so a scan is atomic. Apps hold a store and compose their own writes with record_file() inside transaction().
Example
>>> async with await FeedbackStore.open(Path("feedback.db")) as store:
... await store.record_file_scan(str(path), mtime, candidates)Methods
| Name | Description |
|---|---|
| close() | Closes the underlying connection; a second close is a no-op. |
| dedup_keys() | Returns every stored event’s dedup key. |
| events() | Returns every feedback event, newest first, with the columns needed to render it. |
| execute() | Runs one parameterized write statement, returning the modified-row count. |
| executemany() |
Runs statement once per parameter set, returning the total modified-row count.
|
| executescript() | Runs a multi-statement script — refused while a transaction is open. |
| file_mtimes() |
Returns the recorded path to mtime map for incremental scans.
|
| insert_candidates() |
INSERT OR IGNOREs candidate rows, returning the newly inserted dedup keys.
|
| judged() |
Returns events joined with their (role, prompt_version) verdicts, oldest first.
|
| last_insert_rowid() | Returns the rowid of the last inserted row on this connection. |
| open() |
Opens (creating if needed) the feedback database at path.
|
| recent() | Returns the most recent feedback events, newest first. |
| record_file() |
Upserts the recorded mtime for path.
|
| record_file_scan() | Records a scanned file and its candidates in one transaction. |
| record_verdict() |
Records one verdict, idempotently, keyed by (dedup_key, role, prompt_version).
|
| sql() | Runs one parameterized statement, returning rows as dicts. |
| stats() | Returns ingestion counts by source kind and the scanned-file count. |
| transaction() | Yields the store inside a single committed transaction. |
| unjudged() |
Returns events lacking a verdict for (role, prompt_version), unjudged first.
|
close()
Closes the underlying connection; a second close is a no-op.
Usage
close()Any later use of the store — or of a retained engine reference — raises sqlite3.ProgrammingError, exactly like a closed sqlite3.Connection.
dedup_keys()
Returns every stored event’s dedup key.
Usage
dedup_keys()events()
Returns every feedback event, newest first, with the columns needed to render it.
Usage
events(*, source_kind=None)Parameters
source_kind: SourceKind | None = None- When set, restrict to this source kind.
Returns
list[dict[str, object]]-
One dict per event with its
id,source_kind,occurred_at,text,payload_json,context_json,event_uuid, andsession_id.
execute()
Runs one parameterized write statement, returning the modified-row count.
Usage
execute(statement, params=())executemany()
Runs statement once per parameter set, returning the total modified-row count.
Usage
executemany(statement, seq)executescript()
Runs a multi-statement script — refused while a transaction is open.
Usage
executescript(script)file_mtimes()
Returns the recorded path to mtime map for incremental scans.
Usage
file_mtimes()insert_candidates()
INSERT OR IGNOREs candidate rows, returning the newly inserted dedup keys.
Usage
insert_candidates(rows, extras=None)judged()
Returns events joined with their (role, prompt_version) verdicts, oldest first.
Usage
judged(*, role, prompt_version)The physical accepted/summary columns are aliased to the generic accepted and summary keys, whatever the schema names them.
Parameters
role: str-
The verdict role to join.
prompt_version: int- The prompt version to join.
Returns
list[dict[str, object]]-
One dict per verdict-bearing event: the event columns plus the
verdict’s
category,accepted,confidence,summary,rationale, andmodel.
last_insert_rowid()
Returns the rowid of the last inserted row on this connection.
Usage
last_insert_rowid()open()
Opens (creating if needed) the feedback database at path.
Usage
open(path, schema=None, *, readonly=False, busy_timeout_ms=None, extensions=())Parameters
path: Path-
The database file path; its parent is created if absent.
schema: StoreSchema | None = None-
The app’s schema composition; the platform default when omitted.
readonly: bool = False-
When True, open read-only after exact schema attestation.
busy_timeout_ms: int | None = None-
The SQLite busy timeout; the 5000ms default when omitted.
extensions: Sequence[str] = ()- Loadable SQLite extensions required by the exact schema.
Returns
Self- The opened store.
recent()
Returns the most recent feedback events, newest first.
Usage
recent(*, source_kind=None, limit=20)Parameters
source_kind: SourceKind | None = None-
When set, restrict to this source kind.
limit: int = 20- The maximum number of rows to return.
Returns
list[dict[str, object]]-
One dict per event with its
source_kind,occurred_at, andtext.
record_file()
Upserts the recorded mtime for path.
Usage
record_file(path, mtime)Called inside transaction() by the owning task it joins that transaction; called on its own it commits immediately. A standalone call during another task’s transaction raises TransactionConflictError.
Raises
TransactionConflictError- Another task’s transaction is open.
record_file_scan()
Records a scanned file and its candidates in one transaction.
Usage
record_file_scan(path, mtime, candidates)Inserts every candidate with INSERT OR IGNORE keyed by its dedup key and upserts the file’s mtime, so re-scanning an unchanged file is a no-op.
Parameters
path: str-
The scanned file’s path.
mtime: float-
The file’s modification time at scan.
candidates: Sequence[FeedbackCandidate]- The candidates extracted from the file.
Returns
int- The number of newly inserted feedback events.
record_verdict()
Records one verdict, idempotently, keyed by (dedup_key, role, prompt_version).
Usage
record_verdict(key, verdict, *, role, prompt_version, model, fidelity)model is provenance only, never part of the identity: re-recording is a no-op, except a 'full'-fidelity verdict replaces a 'summary' one at the same key (any model), carrying the new model, content, and canonical_key across. When the engine reports a row changed, the judged event’s sqlite-vec evidence is upserted (or cleared when the verdict names no canonical_key) in the same transaction.
Parameters
key: DedupKey-
The judged event’s dedup key.
verdict: VerdictLike-
The structured verdict to persist.
role: str-
Who produced it, e.g.
judgeorauditor. prompt_version: int-
The prompt version that produced it.
model: str-
The resolved model name that produced it, kept as provenance.
fidelity: Fidelity-
Whether the judged window rendered at
'full'fidelity or from'summary'previews.
sql()
Runs one parameterized statement, returning rows as dicts.
Usage
sql(statement, params=())stats()
Returns ingestion counts by source kind and the scanned-file count.
Usage
stats()transaction()
Yields the store inside a single committed transaction.
Usage
transaction()Composes consumer writes with record_file() so they commit or roll back together. A standalone write by the same task joins this transaction; the transaction is exclusive, so opening another while one is in flight raises TransactionConflictError.
Yields
AsyncIterator[Self]- The store.
Raises
TransactionConflictError- A transaction is already open.
unjudged()
Returns events lacking a verdict for (role, prompt_version), unjudged first.
Usage
unjudged(
*,
role,
prompt_version,
limit=None,
refresh_summary=False,
probe_hydration=True
)Truly-unjudged events sort ahead of summary-refresh rows, then by event id. With refresh_summary set, events whose verdict was recorded at fidelity='summary' re-yield for a full re-judge; a summary row whose context window no longer hydrates is dropped (unless probe_hydration is False). An event filter, when configured, pages a bounded window past dead summary rows and short-circuits limit=0 to empty; the unfiltered shape loads the candidates and returns its off-by-one first row for limit=0 — the pinned observable divergence between the two shapes.
Parameters
role: str-
The verdict role to check.
prompt_version: int-
The prompt version the verdict must carry.
limit: int | None = None-
When set, the maximum number of rows to return.
refresh_summary: bool = False-
When True, also re-yield summary-fidelity rows.
probe_hydration: bool = True- When True, drop summary-refresh rows whose window no longer hydrates; when False, skip the per-row transcript probe.
Returns
list[dict[str, object]]- One dict per event with the columns needed to build its prompt.
mining.StoreSchema
One complete exact v1 feedback-store schema.
Usage
mining.StoreSchema(
identity="cc-transcript-feedback",
ddl=DEFAULT_SCHEMA_DDL,
event_columns=(),
verdict_table="verdicts",
accepted_column="accepted",
summary_column="summary",
event_filter=None
)Attributes
identity: str-
The product-owned identity recorded in the exact v1 marker.
ddl: str-
Complete one-shot application DDL, excluding the platform marker.
event_columns: tuple[str, …]-
Product column names appended to candidate insert rows.
verdict_table: str-
The physical verdict table name.
accepted_column: str-
The verdict table’s accept column name.
summary_column: str-
The verdict table’s summary column name.
event_filter: str | None-
A SQL predicate over alias
eANDed into unjudged and judged, e.g."e.quarantined_reason IS NULL".
mining.TransactionConflictError
A write attempted while another task’s transaction holds the connection.
Usage
mining.TransactionConflictError()The store shares one SQLite connection, so a transaction is exclusive: a standalone write from a different task or thread must not silently join it (a rollback would take the bystander’s committed-looking write with it). Callers hitting this serialize their writes or retry after the open transaction finishes.
mining.Stats
A snapshot of ingestion progress.
Usage
mining.Stats(total, files, by_source)Attributes
total: int-
The total feedback events ingested.
files: int-
The number of scanned files recorded.
by_source: Mapping[str, int]- Event counts keyed by source kind.
mining.sample_windows()
Sample up to n triggerless context windows as steering negatives.
Usage
mining.sample_windows(
raw,
*,
n,
exclude=(),
exclusion_radius=6,
seed=0,
before=6,
after=2,
preview_chars=200
)Each window anchors on a completed turn — the agent acted and the user’s next prompt was not a steer we know about — and folds that turn into the window’s context: trigger is None, before ends at the sampled turn and keeps at most before turns, and after is unchanged. The anchor stays the sampled turn’s first meta-bearing event, so consumers key negatives exactly like positives.
Candidates are every turn carrying at least one event with resolvable meta (the anchor), except the session’s final turn, which may still be in flight. Every candidate in the exclusion_radius turns leading up to an exclude ref’s turn is dropped; refs that no longer resolve are ignored. The draw is a Rust-native deterministic sample keyed on f"{seed}:{session_id}" — stable across processes and releases from this version on, so one seed always yields the same windows for a session.
Parameters
raw: bytes-
The session’s transcript bytes to sample from.
n: int-
The maximum number of windows to return.
exclude: Iterable[EventRef] = ()-
Anchors of known positives to keep clear of.
exclusion_radius: int = 6-
How many turns before each excluded turn to drop (the excluded turn itself included). Turns after an excluded turn stay eligible — once the user has steered, letting the agent run again is a genuine negative; only the pre-steer approach, which positive-window rewinds occupy, is label-conflicted.
seed: int = 0-
The determinism seed, mixed with the session id.
before: int = 6-
How many turns each window’s folded before keeps, ending at the sampled turn.
after: int = 2-
How many turns after each sampled turn to capture.
preview_chars: int = 200- The per-chunk preview budget persisted on each window.
Returns
list[ContextWindow]- The sampled windows, sorted by sampled turn index.