feat(409): the reporting reflex reaches every surface a session reads (#4012)
CI & Build / Python lint (push) Successful in 3s
CI & Build / Plugin hooks (push) Successful in 12s
CI & Build / integration (push) Successful in 53s
CI & Build / TypeScript typecheck (push) Successful in 55s
CI & Build / Python tests (push) Successful in 1m28s
CI & Build / Build & push image (push) Successful in 31s

Step 3 of milestone 409 "Response shapes". Step 2's reporting-back skill
only helps if it fires, and only exists in the Claude Code plugin.

- scribe_static_context.md and using-scribe (new reflex 11) say: report back
  in a shape the operator can read, placed from the `placement` block rather
  than memory, and point at the reporting-back skill. using-scribe also lists
  it among the sibling process-skills.
- update_task returns a one-line `report_back` cue when a task is closed
  (done or cancelled). A tool response is the only surface every MCP client
  sees, at the moment the report is about to be written.
- _INSTRUCTIONS takes no line: there is no budget without trading out a
  session-start reflex. The decision is recorded in server.py's comment
  block so it is not re-litigated blind.
- Guards: test_instruction_surfaces_agree pins the reflex and the placement
  pointer on both plugin surfaces; a tool test pins the cue on closing
  statuses and its absence on every other update.

Plugin version minted.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
This commit is contained in:
2026-09-14 10:11:58 -04:00
co-authored by Claude Opus 5
parent b4dbc495fe
commit 7239e3c479
7 changed files with 112 additions and 4 deletions
+7
View File
@@ -34,6 +34,13 @@ from quart import Quart
# competing for the last ~68 characters, so an addition here is a trade, never
# an append.
#
# Milestone 409 step 3 (reporting back to the operator in a readable shape)
# took NO line here, deliberately: there is no room without trading out a
# session-start reflex, and the moment it applies is when a task closes. So it
# rides in-band instead — update_task returns `placement` and a one-line
# `report_back` cue on done/cancelled, which every MCP client sees — with the
# full shapes in the reporting-back skill and the static context.
#
# Milestone 317 (a note's own verify_with / expires_when, and the sweep over
# them) was DECLINED a line, deliberately, by the operator — not overlooked.
# The reasoning, so it is not re-litigated blind: this is a map, and its own
+18 -1
View File
@@ -271,6 +271,9 @@ async def update_task(
work sits and what comes next — read them from here rather than
reconstructing them, because a remembered milestone title or "next step"
reads exactly like a real one when it is wrong.
Closing a task (done or cancelled) also returns `report_back`: a one-line
reminder of what the reply to the operator should cover.
"""
uid = current_user_id()
fields: dict = {}
@@ -308,7 +311,10 @@ async def update_task(
await systems_tools.attach_systems(
uid, getattr(note, "user_id", uid) or uid, data, task_id, note.project_id
)
return await placement_svc.attach_placement(uid, data, note)
await placement_svc.attach_placement(uid, data, note)
if status in _CLOSING_STATUSES:
data["report_back"] = REPORT_BACK_CUE
return data
async def add_task_log(task_id: int, content: str) -> dict:
@@ -344,6 +350,17 @@ async def add_task_log(task_id: int, content: str) -> dict:
return data
# The in-band half of milestone 409 step 3. The reporting-back skill and the
# static context carry the full shapes, but both live only in the Claude Code
# plugin; a tool response reaches every MCP client, at the moment a piece of
# work closes, which is exactly when the report is about to be written. One
# line on purpose: a template here would be read as the reply itself.
_CLOSING_STATUSES = ("done", "cancelled")
REPORT_BACK_CUE = (
"Reporting this to the operator? Say where it sits (from `placement`), "
"what now works, what needs them, and what comes next."
)
_ITEM_KEYS = {"title", "body", "type", "status", "priority", "kind", "tags", "system_ids"}