Skip to main content

chap-analytics

PyPI Python Licence

A CHAP audit chain, as pandas tables.

A CHAP chain records what people decided about agent work: what an agent produced, what a person changed, why, under which rule, and when. That is a human-labelled evaluation set with provenance, generated as a side effect of ordinary review. This package projects the chain into eleven documented tables so it can be analysed as one.

It is stage one of the analytics roadmap and it stops at the tables. Statistics belong in a layer above, where their assumptions can be stated.

Install

pip install chap-analytics

Python 3.10 or later. pandas is the only dependency. Reading a chain from a live Coordinator in the same process, or generating the sample week, needs chap-coordinator as well:

pip install 'chap-analytics[coordinator]'

Reading a SQLite file, a JSON export or an HTTP endpoint needs the base install alone.

Quick start

from chap_analytics import from_sqlite, frames

chain = from_sqlite("./chap.db", workspace="wsp_support")
f = frames(chain)

print(f.summary())

# Which part of the output do reviewers keep correcting?
f.patch_ops.groupby("top_path").size().sort_values(ascending=False)

# Does the agent's confidence track its outcomes?
f.tasks.groupby("outcome")["confidence"].describe()

# Are reviewers refining the agent's decision or reversing it?
f.overrides.intent_preserved.value_counts(dropna=False)

# Everything, as files.
f.to_csv("./week")

A worked week

chap_analytics.sample.support_desk() generates a week at a support desk against a real coordinator. An agent drafts replies to customer tickets and three people review them. They approve most, correct some and send a few back. One declares a conflict of interest, work changes hands at a shift change, the agent asks two questions and gets one answer, a policy exception goes to a vote, and one ticket is escalated to legal. Every action is a CHAP envelope, and the chain comes back ready to project.

from chap_analytics import frames
from chap_analytics.sample import support_desk

f = frames(support_desk())

Two guided versions of the same week ship with the repository:

The week is generated on each run, so it reflects the coordinator that is installed. The test suite runs both.

The tables

Table Grain
events one row per audit log entry
tasks one row per task
decisions one row per approve, reject, override or abstain
overrides one row per correction, with its diff summarised
patch_ops one row per RFC 6902 operation within an override
participants one row per participant per workspace
deliberations one row per group decision
votes one row per vote
whispers one row per deadline-bound question
handoffs one row per proposed handoff
routing one row per routing decision

Every column is declared in schema.py with its dtype and its provenance. describe() prints the whole contract:

from chap_analytics import describe
print(describe())

A column the source lacked a value for is present and null. Code downstream can reference any column and see missingness rather than a KeyError.

Four sources

from chap_analytics import from_sqlite, from_url, from_json, from_coordinator

from_sqlite("./chap.db", workspace="wsp_support")     # the full snapshot
from_url("http://localhost:8080/chap", "wsp_support") # envelopes, via audit.read
from_json("export.json")                              # an audit.read result, a bare entry list, or a snapshot
from_coordinator(coord, workspace="wsp_support")      # a live coordinator, in-process

A SQLite file or a live coordinator carries the workspace snapshot: every task, override, deliberation and handoff as the coordinator holds it, and the audit log beside them. audit.read, which is what an MCP client can obtain, returns the audit log alone: every request parameter, in order, and hash-linked where the coordinator chains its log.

The tables are replayed from the envelope stream, so most of what is worth analysing is available from either source. The artefact under review arrives on review.request and the patch on decide.override, so the before and the after of every correction come from the envelopes themselves. Three things come from the snapshot alone: deliberation and routing outcomes, which the server computes; the assignee a task.route chose, which assignee_certain flags; and the certainty of which server-minted id belongs to which creation, which the section on identity below explains.

How far the two reads agree is measured. Random workspaces are driven against a real coordinator and read both ways. For every task, decision, override, whisper, deliberation and handoff, the row either matches what the coordinator holds or is marked id_certain false.

What the projection handles for you

Parses confidence. Fractional values travel the CHAP wire as decimal strings, because canonicalisation admits integers alone (SPECIFICATION §7). The tables carry a float.

Censors open work. lifetime_s is null while a task is unsettled, and settled says which rows are censored. A mean over the finished work alone flatters every latency figure; the flag lets you say so.

Knows when a decision settled a review. Under all_approve or quorum:N an approval may leave the review open. is_final is computed with the rule the coordinator applies. Under all_approve the coordinator waits on the reviewers it can name, and a review addressed to a group alone has none to wait on, so it treats that review as first-approve.

Separates the review passes. A task sent back for revision is reviewed again, and the coordinator starts that review afresh. review_index says which pass a decision belongs to, n_reviews counts the passes, latency_s is measured from each pass's own opening, and outcome is read from the decision that settled the last pass.

Reconstructs the correction. based_on is the artefact the reviewer saw and result is the patch applied to it, using the coordinator's own RFC 6902 semantics. Both are available from audit.read alone. Where the snapshot carries the artefact the coordinator stored, that one is used, and the differential suite requires the replayed one to equal it.

Counts the tasks the server minted. escalate.raise and control.supersede create a successor without a task.create envelope, and give it what the original had. An escalation successor takes the original's mode, and its kind where the spec omits one. A supersession successor takes the original's assignee and mode where the spec omits them, and gets the same review_required default a task.create would. The successor is a row like any other, with supersedes linking it back.

Keeps the orphans. A task created and left alone still gets a row.

Says how sure it is. id_certain and assignee_certain mark the rows where the chain admits more than one reading, so the population to draw conclusions from is a filter away.

Redaction

Artefacts hold whatever the agent was working on: customer messages, contracts, source code. A redactor sees every one of them before anything reaches a table.

from chap_analytics import from_sqlite, redact_artefacts, frames

f = frames(from_sqlite("./chap.db", redact=redact_artefacts))

What goes: task inputs and outputs, the artefact under review and the corrected one, the values a patch writes, free-text whisper answers under either of their two names, lapse defaults, the inputs of a successor an escalation or supersession mints, and the copies a snapshot holds. All of it goes in both the envelope stream and the workspace snapshot that also contains it. A test plants a marker in each of those places and searches every cell of every table for all of them.

What stays, because it is the analysis: counts, rates, latencies, tags, policy references, patch paths and operations, which option a whisper answer chose, and the words participants wrote about the work. A reviewer's rationale, comment and decline reason, a handoff summary and a deliberation question are the people's account of what they did, and the point of the chain. A whisper question is the agent's own words to a person, and it stays too; an agent that quotes customer content into a question puts that content in the log, and a deployment that needs it removed strips the question field before loading.

Identity

Server-minted identifiers are returned in the result of a call, and the audit log records the envelopes. A task id therefore becomes visible when a later envelope acts on it, and which creation produced which id is worked out afterwards.

With the snapshot, the pairing is settled by what the creation envelope and the stored task agree they are, with the order of appearance breaking ties. From envelopes alone, the order of appearance is the evidence: it settles the pairing when work proceeds one task at a time, and leaves it open when two tasks are created before either is touched. The row says which case it is in. id_certain is true where the evidence leaves one reading, so

f.tasks[f.tasks.id_certain]

is the population to draw conclusions about individual tasks from. Counts hold either way: a creation that matches no observed id is given one marked unidentified, and the total stays right.

The protocol settles more than the ordering can. A lapse notice concerns a whisper whose deadline had passed. An answer comes from someone the whisper was addressed to. A vote comes from someone the deliberation invited. An acceptance comes from the named recipient. An id the caller supplied is in the opening envelope itself. Each of those is used before the ordering is consulted, and frames.summary() reports how many rows remain inferred.

Tests

pip install -e ".[dev]"
pytest

The fixtures drive a real coordinator. A generated fixture captures what the coordinator does, and fails when the protocol moves.

Alongside the written cases there is a differential suite: random sequences of accepted calls against a real coordinator, read both ways. The tasks, decisions, overrides, whispers, deliberations and handoffs are checked against what that coordinator holds, the corrected artefacts against the ones it stored, and a floor is asserted on how many envelope-only rows are certain.

Licence

Apache 2.0.

Download files

Download the file for your platform. If you're not sure which to choose, learn more about installing packages.

Source Distribution

chap_analytics-0.1.0.tar.gz (79.8 kB view details)

Uploaded Source

Built Distribution

If you're not sure about the file name format, learn more about wheel file names.

chap_analytics-0.1.0-py3-none-any.whl (51.5 kB view details)

Uploaded Python 3

File details

Details for the file chap_analytics-0.1.0.tar.gz.

File metadata

  • Download URL: chap_analytics-0.1.0.tar.gz
  • Upload date:
  • Size: 79.8 kB
  • Tags: Source
  • Uploaded using Trusted Publishing? No
  • Uploaded via: twine/7.0.0 CPython/3.11.15

File hashes

Hashes for chap_analytics-0.1.0.tar.gz
Algorithm Hash digest
SHA256 7531562f49fcba1de7bb22d57de56255eeaa534b1e97998f69ac13db7e467f91
MD5 e8ad3b9afe5a35e47a5fa63d9781d78d
BLAKE2b-256 e37a5435f355b50802fe3d18adf88c0fe0d11e438e0279b416866858835e9c58

See more details on using hashes here.

File details

Details for the file chap_analytics-0.1.0-py3-none-any.whl.

File metadata

  • Download URL: chap_analytics-0.1.0-py3-none-any.whl
  • Upload date:
  • Size: 51.5 kB
  • Tags: Python 3
  • Uploaded using Trusted Publishing? No
  • Uploaded via: twine/7.0.0 CPython/3.11.15

File hashes

Hashes for chap_analytics-0.1.0-py3-none-any.whl
Algorithm Hash digest
SHA256 0d20b9ebf13369d82757e589ed7f3d715c329fb6111f43e452b1ef588310777c
MD5 47da8aa7dfadae56b10e82e925eeeae2
BLAKE2b-256 b220ebb086b6209f31aa520630dc08881f7bb94288bd0be3530371a243cbbc8a

See more details on using hashes here.

Release history Release notifications | RSS feed

This release

0.1.0 This release

2 files

Anthropic, PBC Visionary sponsor Bloomberg Visionary sponsor Hudson River Trading Visionary sponsor Meta Visionary sponsor NVIDIA Visionary sponsor Microsoft Sustainability sponsor Depot Continuous Integration AWS Cloud computing and Security Sponsor Datadog Monitoring Fastly CDN Google Download Analytics Sentry Error logging StatusPage Status page