comicbox.events

source module comicbox.events

Shared event types for read / write / online workflows.

One on_event callback shape across the three public bulk entry points (iter_process_files, bulk_write, OnlineSession.tag_many) so a caller — typically Codex — can wire a single handler.

Handlers run on the orchestrator thread that drains worker results. They must be thread-safe and should return quickly; expensive work belongs in a queue the handler dispatches to.

Classes

  • Event Base for all workflow events.

  • BatchStarted Fired once before the first file is submitted.

  • FileShortCircuited Worker decided the embedded metadata had not changed and skipped parse.

  • FileParsed Worker fully parsed embedded metadata.

  • FileError Worker raised an exception. error is the str(exception).

  • BatchFinished Fired once after every file has been delivered.

  • SearchStarted Worker is about to query an online source for this file.

  • SearchCompleted Online source returned candidate list.

  • AutoWritten Matcher accepted a candidate without prompting.

  • SeriesIdentified A series was resolved and cached for series-first batching.

  • PromptQueued Matcher needs human input; prompt dispatched to PromptHandler.

  • PromptResolved PromptHandler returned a decision.

  • PromptDeferred OnlineSession is in defer_prompts mode and queued this prompt for later.

  • PromptResolvedFromCache OnlineSession auto-applied a cached choice instead of re-prompting.

  • Skipped Matcher declined to write metadata for this file.

  • NoMatch No candidate cleared min_confidence.

  • RateLimited Source signaled a rate-limit hit.

  • FileFinished Per-file online tagging completed (success, skip, no-match, or error).

source dataclass Event(*, kind: str, path: Path | None = None, index: int | None = None, total: int | None = None)

Base for all workflow events.

Subclasses are concrete frozen dataclasses keyed by the kind literal on the subclass — callers can match on type or on kind.

source dataclass BatchStarted(*, kind: Literal[‘batch_started’] = ‘batch_started’, path: Path | None = None, index: int | None = None, total: int | None = None)

Bases : Event

Fired once before the first file is submitted.

source dataclass FileShortCircuited(*, kind: Literal[‘file_short_circuited’] = ‘file_short_circuited’, path: Path | None = None, index: int | None = None, total: int | None = None, reason: Literal[‘mtime_unchanged’, ‘filtered’, ‘dry_run’] = ‘mtime_unchanged’)

Bases : Event

Worker decided the embedded metadata had not changed and skipped parse.

reason="mtime_unchanged" — embedded mtime <= caller’s old_mtime. reason="filtered" — caller passed full_metadata=False so the worker only collected envelope fields.

source dataclass FileParsed(*, kind: Literal[‘file_parsed’] = ‘file_parsed’, path: Path | None = None, index: int | None = None, total: int | None = None)

Bases : Event

Worker fully parsed embedded metadata.

source dataclass FileError(*, kind: Literal[‘file_error’] = ‘file_error’, path: Path | None = None, index: int | None = None, total: int | None = None, error: str = ‘’)

Bases : Event

Worker raised an exception. error is the str(exception).

source dataclass BatchFinished(*, kind: Literal[‘batch_finished’] = ‘batch_finished’, path: Path | None = None, index: int | None = None, total: int | None = None, parsed: int = 0, short_circuited: int = 0, errored: int = 0)

Bases : Event

Fired once after every file has been delivered.

parsed / short_circuited / errored carry per-outcome counts so callers can render an end-of-batch summary without re-walking the result stream.

source dataclass SearchStarted(*, kind: Literal[‘search_started’] = ‘search_started’, path: Path | None = None, index: int | None = None, total: int | None = None, source: str = ‘’)

Bases : Event

Worker is about to query an online source for this file.

source dataclass SearchCompleted(*, kind: Literal[‘search_completed’] = ‘search_completed’, path: Path | None = None, index: int | None = None, total: int | None = None, source: str = ‘’, n_candidates: int = 0, top_score: float | None = None)

Bases : Event

Online source returned candidate list.

source dataclass AutoWritten(*, kind: Literal[‘auto_written’] = ‘auto_written’, path: Path | None = None, index: int | None = None, total: int | None = None, source: str = ‘’, candidate_summary: str = ‘’)

Bases : Event

Matcher accepted a candidate without prompting.

source dataclass SeriesIdentified(*, kind: Literal[‘series_identified’] = ‘series_identified’, path: Path | None = None, index: int | None = None, total: int | None = None, source: str = ‘’, series_fingerprint: str = ‘’, volume_id: int = 0)

Bases : Event

A series was resolved and cached for series-first batching.

Fired when a cold-path search resolves a series and the session’s series_cache is populated for the first time. Subsequent issues of the same series skip the search via :meth:OnlineSource.lookup_issue.

source dataclass PromptQueued(*, kind: Literal[‘prompt_queued’] = ‘prompt_queued’, path: Path | None = None, index: int | None = None, total: int | None = None, source: str = ‘’, prompt_id: str = ‘’, n_candidates: int = 0)

Bases : Event

Matcher needs human input; prompt dispatched to PromptHandler.

source dataclass PromptResolved(*, kind: Literal[‘prompt_resolved’] = ‘prompt_resolved’, path: Path | None = None, index: int | None = None, total: int | None = None, source: str = ‘’, prompt_id: str = ‘’, action: str = ‘’)

Bases : Event

PromptHandler returned a decision.

source dataclass PromptDeferred(*, kind: Literal[‘prompt_deferred’] = ‘prompt_deferred’, path: Path | None = None, index: int | None = None, total: int | None = None, source: str = ‘’, prompt_id: str = ‘’, fingerprint: str = ‘’, n_candidates: int = 0)

Bases : Event

OnlineSession is in defer_prompts mode and queued this prompt for later.

The batch continues; the deferred prompt is accessible via OnlineSession.deferred_prompts(). Codex’s intended flow: surface these in a “Review tagging” UI after the batch completes, collect the user’s decisions, seed them back into the session’s dedup cache via preload_resolution(), then re-run the affected files.

source dataclass PromptResolvedFromCache(*, kind: Literal[‘prompt_resolved_from_cache’] = ‘prompt_resolved_from_cache’, path: Path | None = None, index: int | None = None, total: int | None = None, source: str = ‘’, prompt_id: str = ‘’, action: str = ‘’, fingerprint: str = ‘’)

Bases : Event

OnlineSession auto-applied a cached choice instead of re-prompting.

Fingerprint matched a previously-resolved prompt in the per-session dedup cache; the user’s handler was not invoked.

source dataclass Skipped(*, kind: Literal[‘skipped’] = ‘skipped’, path: Path | None = None, index: int | None = None, total: int | None = None, source: str = ‘’, reason: str = ‘’)

Bases : Event

Matcher declined to write metadata for this file.

source dataclass NoMatch(*, kind: Literal[‘no_match’] = ‘no_match’, path: Path | None = None, index: int | None = None, total: int | None = None, source: str = ‘’)

Bases : Event

No candidate cleared min_confidence.

source dataclass RateLimited(*, kind: Literal[‘rate_limited’] = ‘rate_limited’, path: Path | None = None, index: int | None = None, total: int | None = None, source: str = ‘’, retry_after_seconds: float | None = None)

Bases : Event

Source signaled a rate-limit hit.

retry_after_seconds is the server hint when available; otherwise the per-source retry-schedule’s next sleep.

source dataclass FileFinished(*, kind: Literal[‘file_finished’] = ‘file_finished’, path: Path | None = None, index: int | None = None, total: int | None = None, outcome: str = ‘’)

Bases : Event

Per-file online tagging completed (success, skip, no-match, or error).