From 1aa1afca1feb7d26052cf110f8d2d5069f83e7b0 Mon Sep 17 00:00:00 2001 From: srikrishnavansi Date: Mon, 12 Oct 2026 22:06:18 +0530 Subject: [PATCH 1/2] docs: say what a lapsed whisper does and does not show A lapse records that no answer arrived before the deadline and which default was applied. It does not show that the person was notified or chose not to answer, since delivery is left to the deployment. Adjust the whisper profile (section 4), example 06 and two chap-analytics strings that read a lapse as a person not responding. Refs #180 Co-Authored-By: Claude Opus 5.5 --- examples/06-whisper-prompt.md | 14 +++++++++----- packages/chap-analytics/chap_analytics/briefs.py | 2 +- packages/chap-analytics/chap_analytics/charts.py | 3 ++- profiles/whisper.md | 15 +++++++++++++-- 4 files changed, 25 insertions(+), 9 deletions(-) diff --git a/examples/06-whisper-prompt.md b/examples/06-whisper-prompt.md index 5292ff0..a9d432d 100644 --- a/examples/06-whisper-prompt.md +++ b/examples/06-whisper-prompt.md @@ -126,8 +126,11 @@ T+60s the Coordinator would emit: ``` The agent proceeds with the default. The lapse is recorded as a -first-class evidence entry, so the auditor can see exactly when a -human declined to be reachable for a given decision. +first-class evidence entry, so the auditor can see exactly when no +answer arrived before the deadline and which default was applied. It +does not say why: the chain cannot tell a person who saw the question +and let it lapse from one who was never notified, because delivery is +left to the deployment (see [`profiles/whisper.md`](../profiles/whisper.md) §4). --- @@ -157,9 +160,10 @@ task's `complete`. responsiveness. - **Whisper analytics.** "What fraction of cancellations involve a whisper?" is a single audit query. -- **Visible accountability for non-response.** A lapsed whisper is - recorded with the timestamp and the default applied; nothing - silently disappears. +- **Visible lapses.** A lapsed whisper is recorded with the + timestamp and the default applied, so nothing silently disappears. + The record shows that no answer arrived in time, not that the + person chose not to answer. Move on to [`07-handoff-shift-change.md`](./07-handoff-shift-change.md) for transferring an in-progress task between humans. diff --git a/packages/chap-analytics/chap_analytics/briefs.py b/packages/chap-analytics/chap_analytics/briefs.py index 5c6666d..f6e4f15 100644 --- a/packages/chap-analytics/chap_analytics/briefs.py +++ b/packages/chap-analytics/chap_analytics/briefs.py @@ -301,7 +301,7 @@ def whispers(f: Frames) -> Brief: if not r["sufficient"]: decision = "Too few resolved questions to judge; the lapse rate will settle as more are asked." elif r["lapse_rate"] > 0.25: - decision = "More than a quarter lapse: the default answer is deciding those questions. Put the answer in the task input, or route whispers to whoever is at the desk." + decision = "More than a quarter lapse: the default answer is deciding those questions. Put the answer in the task input, check the question reaches someone, or route whispers to whoever is at the desk." else: decision = "Questions are being answered in time." return Brief("whispers", "Whether agents get answers", head, text, decision, bool(r["sufficient"]), w) diff --git a/packages/chap-analytics/chap_analytics/charts.py b/packages/chap-analytics/chap_analytics/charts.py index fef47b5..f3f8237 100644 --- a/packages/chap-analytics/chap_analytics/charts.py +++ b/packages/chap-analytics/chap_analytics/charts.py @@ -629,7 +629,8 @@ def whispers(w: pd.DataFrame, by: str = "asker") -> Chart: """Lapse rate per asker with intervals. Takes :func:`stats.whispers` output.""" name = "whispers" q = "How often do agents have to ask, and does anyone answer in time?" - d = "A high lapse rate is a question the task input should have answered, or a reviewer who is never at the desk." + d = ("A high lapse rate is a question the task input should have answered, or one that did not reach " + "anyone in time. The chain records that no answer arrived before the deadline, not why.") if w.empty: return _empty(name, "Whispers that lapsed", q, d, "No whispers in this chain.") key = by if by in w.columns else None diff --git a/profiles/whisper.md b/profiles/whisper.md index cf78cb6..2cb6e87 100644 --- a/profiles/whisper.md +++ b/profiles/whisper.md @@ -125,8 +125,19 @@ no human input) is visible on an `audit.read` filtered by task: ``` The asker proceeds with the default. A lapsed whisper is itself -audit data, "this human was unreachable for this decision" is now -queryable. +audit data: "no answer was recorded before the deadline, and this +default was applied" is now queryable by task. + +**What a lapse does and does not show.** The lapse record shows who +asked whom, when, the deadline, that no `whisper.answer` was recorded +before the lapse check ran, and which default was applied. It does not +show that the addressed humans were notified, that they saw the +question, or that they chose not to answer. CHAP leaves delivery to the +deployment and no reference Coordinator implements a delivery layer, so +a person who was never told and a person who saw the question and let +it pass produce the same record. Read a lapse as "no answer in time", +not as a decision by a person. A deployment that needs to tell the two +apart has to record delivery itself. --- From e21dde57df711f0585175e851e7d7db5d23829e2 Mon Sep 17 00:00:00 2001 From: Arsalan Shahid Date: Wed, 14 Oct 2026 11:06:51 +0100 Subject: [PATCH 2/2] docs: say which whisper entry records what, and drop the delivery claims in example 06 - profiles/whisper.md section 4: the whisper.ask entry records who asked whom, when and the deadline; the lapse notification records that no answer arrived before the lapse check ran and the default applied. Neither shows whether anyone was notified. Points at SPECIFICATION 15.1, item 10, for delivery. - examples/06-whisper-prompt.md: the Coordinator records the whisper and leaves delivery to the deployment, and the first lapse check after the deadline records the lapse, with no assumption that Alice was away. - chap-analytics: the whispers brief and chart say what to check in plain words, and the example report and the walkthrough output carry the same text. Drafted-by: Claude Opus 5.5 Reviewed-by: Arsalan Shahid Signed-off-by: Arsalan Shahid CHAP-Approval: tsk_01M4WYCTX7VTVQC51MCEZ8HE3A 1/1 --- examples/06-whisper-prompt.md | 31 ++++++++++--------- .../chap-analytics/chap_analytics/briefs.py | 2 +- .../chap-analytics/chap_analytics/charts.py | 4 +-- .../chap-analytics/docs/example_report.html | 2 +- .../examples/chap_analytics_walkthrough.ipynb | 2 +- profiles/whisper.md | 28 ++++++++--------- 6 files changed, 35 insertions(+), 34 deletions(-) diff --git a/examples/06-whisper-prompt.md b/examples/06-whisper-prompt.md index a9d432d..45f59d8 100644 --- a/examples/06-whisper-prompt.md +++ b/examples/06-whisper-prompt.md @@ -65,9 +65,11 @@ options. } ``` -The Coordinator routes this with the **`whisper` channel**: typically -an interrupt-style notification in the human's client, distinct from -normal review queues. The deadline starts ticking. +The Coordinator records the whisper, and its 60-second deadline runs +from this ask. Getting the question in front of Alice, for example as a +notification in her client, is up to the deployment: a Coordinator has +no delivery layer of its own (see +[SPECIFICATION.md §15.1](../SPECIFICATION.md#151-mandatory-protections)). --- @@ -102,8 +104,8 @@ whisper and the answer are recorded in the chain. ## 6.4 What if no one answers? -Whispers are deadline-bound. If Alice had been away from her desk, at -T+60s the Coordinator would emit: +Whispers are deadline-bound. If no answer is recorded within 60 +seconds, the first lapse check after the deadline records: ```json { @@ -125,12 +127,12 @@ T+60s the Coordinator would emit: } ``` -The agent proceeds with the default. The lapse is recorded as a -first-class evidence entry, so the auditor can see exactly when no -answer arrived before the deadline and which default was applied. It -does not say why: the chain cannot tell a person who saw the question -and let it lapse from one who was never notified, because delivery is -left to the deployment (see [`profiles/whisper.md`](../profiles/whisper.md) §4). +The agent proceeds with the default. The lapse is an entry on the +chain, so an auditor can see that no answer came in time and which +default was applied. It does not say why: a person +who saw the question and let it lapse and a person who was never +notified leave the same record, because delivery is left to the +deployment (see [`profiles/whisper.md`](../profiles/whisper.md) §4). --- @@ -160,10 +162,9 @@ task's `complete`. responsiveness. - **Whisper analytics.** "What fraction of cancellations involve a whisper?" is a single audit query. -- **Visible lapses.** A lapsed whisper is recorded with the - timestamp and the default applied, so nothing silently disappears. - The record shows that no answer arrived in time, not that the - person chose not to answer. +- **Visible lapses.** A lapsed whisper is recorded with its time and + the default applied, so an unanswered question stays on the record. + The record shows only that no answer arrived in time. Move on to [`07-handoff-shift-change.md`](./07-handoff-shift-change.md) for transferring an in-progress task between humans. diff --git a/packages/chap-analytics/chap_analytics/briefs.py b/packages/chap-analytics/chap_analytics/briefs.py index f6e4f15..35d22c8 100644 --- a/packages/chap-analytics/chap_analytics/briefs.py +++ b/packages/chap-analytics/chap_analytics/briefs.py @@ -301,7 +301,7 @@ def whispers(f: Frames) -> Brief: if not r["sufficient"]: decision = "Too few resolved questions to judge; the lapse rate will settle as more are asked." elif r["lapse_rate"] > 0.25: - decision = "More than a quarter lapse: the default answer is deciding those questions. Put the answer in the task input, check the question reaches someone, or route whispers to whoever is at the desk." + decision = "More than a quarter lapse: the default answer is deciding those questions. Put the answer in the task input, check that the questions reach the people asked, or address them to someone who can answer in time." else: decision = "Questions are being answered in time." return Brief("whispers", "Whether agents get answers", head, text, decision, bool(r["sufficient"]), w) diff --git a/packages/chap-analytics/chap_analytics/charts.py b/packages/chap-analytics/chap_analytics/charts.py index f3f8237..1386b36 100644 --- a/packages/chap-analytics/chap_analytics/charts.py +++ b/packages/chap-analytics/chap_analytics/charts.py @@ -629,8 +629,8 @@ def whispers(w: pd.DataFrame, by: str = "asker") -> Chart: """Lapse rate per asker with intervals. Takes :func:`stats.whispers` output.""" name = "whispers" q = "How often do agents have to ask, and does anyone answer in time?" - d = ("A high lapse rate is a question the task input should have answered, or one that did not reach " - "anyone in time. The chain records that no answer arrived before the deadline, not why.") + d = ("A high lapse rate points at questions the task input should have answered, or at questions that " + "reach no one in time. The chain records only that no answer arrived before the deadline.") if w.empty: return _empty(name, "Whispers that lapsed", q, d, "No whispers in this chain.") key = by if by in w.columns else None diff --git a/packages/chap-analytics/docs/example_report.html b/packages/chap-analytics/docs/example_report.html index 786b24d..c73dcd5 100644 --- a/packages/chap-analytics/docs/example_report.html +++ b/packages/chap-analytics/docs/example_report.html @@ -701,7 +701,7 @@

Lineage of one task

}); - +