oc_connect
Loading...
Searching...
No Matches
oc_connect.worker_reports Namespace Reference

Classes

class  WorkerReportError
class  WorkerReportRelayError
class  WorkerReportEvent
class  WorkerReportStore
class  RelayOutcome
class  WorkerReportRelay

Functions

str chunk_prefix (int index, int total)
tuple[str,...] chunk_summary (str summary, *, int limit=TELEGRAM_MESSAGE_LIMIT)
str render_chunk (str piece, int index, int total)
str render_event (WorkerReportEvent event)
None _atomic_write (Path path, str payload)

Variables

str WORKER_REPORT_LANE = "worker-reports"
str WORKER_REPORT_SOURCE = "worker"
str WORKER_REPORT_SCHEMA_VERSION = "1.0"
int TELEGRAM_MESSAGE_LIMIT = 4096
int MIN_CHUNK_LIMIT = 32
 WorkerReportStatus = Literal["awaiting_gate", "blocked"]
str WORKER_REPORT_BEGIN = "<<<BEGIN WORKER REPORT {event_id}>>>"
str WORKER_REPORT_END = "<<<END WORKER REPORT {event_id}>>>"
str NEUTRALISED_MARKER = "<< <"

Detailed Description

The worker-report lane: worker events at rest, and the verbatim relay.

Proposal V1 §5 (the ruled test case). A plan worker that reaches AWAITING GATE
or Blocked writes ONE event into ``comms/lanes/worker-reports/`` and stops
there: it holds no credential, knows nothing about connectors, and touches no
other lane. This module is the other half — the orchestrator-side reader and
the grant-checked, egress-filtered relay that puts the worker's own words on
the operator's phone.

**This module is the schema authority for the lane.** The writer lives in
``oc_chat/oc_chat/scripts/plan_status.py`` (a ``report`` action on the existing
worker channel) and deliberately does NOT import this package — that is what
keeps workers credential-free. The two sides therefore agree by a written
contract rather than by a shared import, and the contract is
:class:`WorkerReportEvent`::

    {
      "schema_version":  "1.0",
      "event_id":        "<26-character ULID, time-first so lexical == arrival>",
      "lane":            "worker-reports",
      "direction":       "inbound",
      "risk_class":      "low",
      "plan_id":         "231-worker-report-lane",
      "agent":           "agent-007",
      "status":          "awaiting_gate" | "blocked",
      "worktree":        "<absolute worktree path>",
      "report_path":     "<absolute path of report.md in that worktree>",
      "created_at_utc":  "2026-09-01T12:00:00Z",
      "summary":         "<the worker's FULL closing summary, verbatim>",
      "summary_sha256":  "<sha256 of summary, utf-8>",
      "relayed_at_utc":  null,
      "relay_chunk_count": null
    }

File naming follows the §3 bus convention, ``<utc-stamp>_worker_<ulid>.json``.

Two properties are worth stating because they are easy to lose in a refactor:

* **The egress scan runs on the WHOLE summary, before any chunking.** Scanning
  the rendered chunks instead would let a credential that straddles a chunk
  boundary leave in two safe-looking halves. The relay therefore blocks on the
  joined text and never enqueues anything for an event that failed the scan.
* **Chunk identity is derived from the event id**, so a relay interrupted after
  chunk 3 of 5 resumes without re-sending chunks 1-3: the durable outbox
  already holds them under the same idempotency keys and returns their existing
  receipts instead of re-delivering.

Dependencies
------------
- oc_connect.audit (AuditStore, content_hash, timestamp, utc_now - audits relay outcomes, verifies summary_sha256 and stamps relayed_at_utc)
- oc_connect.delivery (OutboundSender - the grant-checked send path every relayed chunk crosses)
- oc_connect.egress (require_safe, EgressBlockedError - scans the whole summary before any chunking)
- oc_diagnostics (_dbg structured logger)
- option_c_connector_contracts (DeliveryReceipt, lane - chunk receipts and the lane's declared direction and risk ceiling)
- pydantic (BaseModel, ConfigDict, field_validator - the WorkerReportEvent schema)

Function Documentation

◆ _atomic_write()

None oc_connect.worker_reports._atomic_write ( Path path,
str payload )
protected

Definition at line 244 of file worker_reports.py.

Here is the caller graph for this function:

◆ chunk_prefix()

str oc_connect.worker_reports.chunk_prefix ( int index,
int total )
Return the ordering marker prepended to one chunk of a split summary.

Definition at line 173 of file worker_reports.py.

Here is the caller graph for this function:

◆ chunk_summary()

tuple[str, ...] oc_connect.worker_reports.chunk_summary ( str summary,
* ,
int limit = TELEGRAM_MESSAGE_LIMIT )
Split *summary* into pieces that fit *limit* once numbered.

The split is a plain slice, never a word-boundary or whitespace-trimming
split: ``"".join(chunk_summary(text)) == text`` is the property the whole
verbatim guarantee rests on. A summary that already fits comes back as one
piece, and :func:`render_chunk` leaves that single piece unnumbered.

The chunk count and the marker width depend on each other, so the budget is
solved to a fixed point rather than guessed: every marker is rendered at the
widest form (``[{total}/{total}]``) so all pieces share one budget.

Definition at line 178 of file worker_reports.py.

Here is the call graph for this function:
Here is the caller graph for this function:

◆ render_chunk()

str oc_connect.worker_reports.render_chunk ( str piece,
int index,
int total )
Return the message text for one chunk; a lone chunk is unnumbered.

Definition at line 218 of file worker_reports.py.

Here is the caller graph for this function:

◆ render_event()

str oc_connect.worker_reports.render_event ( WorkerReportEvent event)
Return the only representation in which a summary may be displayed.

The body is fenced with the event's own id and any literal ``<<<`` inside
it is neutralised, so a summary cannot forge its own closing fence. The
rendered copy is the only thing that changes; the stored text does not.

Definition at line 225 of file worker_reports.py.

Variable Documentation

◆ MIN_CHUNK_LIMIT

int oc_connect.worker_reports.MIN_CHUNK_LIMIT = 32

Definition at line 90 of file worker_reports.py.

◆ NEUTRALISED_MARKER

str oc_connect.worker_reports.NEUTRALISED_MARKER = "<< <"

Definition at line 103 of file worker_reports.py.

◆ TELEGRAM_MESSAGE_LIMIT

int oc_connect.worker_reports.TELEGRAM_MESSAGE_LIMIT = 4096

Definition at line 87 of file worker_reports.py.

◆ WORKER_REPORT_BEGIN

str oc_connect.worker_reports.WORKER_REPORT_BEGIN = "<<<BEGIN WORKER REPORT {event_id}>>>"

Definition at line 98 of file worker_reports.py.

◆ WORKER_REPORT_END

str oc_connect.worker_reports.WORKER_REPORT_END = "<<<END WORKER REPORT {event_id}>>>"

Definition at line 99 of file worker_reports.py.

◆ WORKER_REPORT_LANE

str oc_connect.worker_reports.WORKER_REPORT_LANE = "worker-reports"

Definition at line 78 of file worker_reports.py.

◆ WORKER_REPORT_SCHEMA_VERSION

str oc_connect.worker_reports.WORKER_REPORT_SCHEMA_VERSION = "1.0"

Definition at line 83 of file worker_reports.py.

◆ WORKER_REPORT_SOURCE

str oc_connect.worker_reports.WORKER_REPORT_SOURCE = "worker"

Definition at line 81 of file worker_reports.py.

◆ WorkerReportStatus

oc_connect.worker_reports.WorkerReportStatus = Literal["awaiting_gate", "blocked"]

Definition at line 92 of file worker_reports.py.