feat(500): reply shapes are delivered - the core every turn through the ledger, each slice at its moment, reply mounts before the reply (#5495)
CI & Build / Python lint (push) Successful in 3s
CI & Build / Plugin hooks (push) Successful in 12s
CI & Build / TypeScript typecheck (push) Successful in 56s
CI & Build / integration (push) Successful in 1m12s
CI & Build / Python tests (push) Failing after 1m31s
CI & Build / Build & push image (push) Skipped

- Every turn (/api/plugin/retrieve, UserPromptSubmit): the core reply shape
  leads the payload - in full the first time, as its one-line reminder
  after that - followed by whatever is mounted on reply.report, under the
  shared rule ledger. Fresh keys come back as shape_keys.
- The ledger is <sid>.shapes.ids in scribe-priorart (scribe_shapes_file /
  _seen / _append), so the compaction sweep that clears every .ids ledger
  is what brings the full core back after one.
- At a moment (/api/plugin/moment): the slice for that reply - completion
  on work.finish, asks on reply.ask, plan on work.plan - ahead of the
  mounted rules. reachable_tools now lists tools reaching a shaped moment
  even on an install with nothing mounted.
- Scribe's own tools (attach_moment_rules): reply_shape in the response,
  in full, since that door has no ledger. enter_project carries the core
  for clients with no prompt hook.
- Telemetry: one AppLog row per delivery (plugin / reply_shape), each
  shape with full or pointer and the door (turn, hook, mcp).

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
This commit is contained in:
2026-10-09 14:45:25 -04:00
co-authored by Claude Opus 5.5
parent 9fedcbea3d
commit eadb08c347
14 changed files with 620 additions and 37 deletions
+29
View File
@@ -1208,6 +1208,35 @@ scribe_written_append() {
return 0
}
# THE REPLY-SHAPE LEDGER (milestone 500). Which default reply shapes this
# session has been shown IN FULL — `core`, `completion`, `asks`, `plan` — one
# key per line in <sid>.shapes.ids under the prior-art directory. The server
# sends a shape in full when its key is absent and as a one-line reminder when
# present, so the core arrives whole once and is a pointer on later turns.
#
# Swept with every other `.ids` ledger on compact and clear, and that is the
# point rather than a side effect: after a compaction the session no longer
# holds the shape, so the next turn gets it in full again. Not aged — the
# per-turn reminder is what keeps it in view between compactions.
# scribe_shapes_file <sid> the ledger's path
# scribe_shapes_seen <file> its keys, comma-joined, for `shapes_seen=`
# scribe_shapes_append <file> keys on stdin, appended
scribe_shapes_file() {
printf '%s/scribe-priorart/%s.shapes.ids' "${TMPDIR:-/tmp}" "$1"
}
scribe_shapes_seen() {
[ -n "$1" ] && [ -f "$1" ] || return 0
awk '/^[a-z]+$/ && !seen[$0]++ { out = out (out == "" ? "" : ",") $0 } END { print out }' \
"$1" 2>/dev/null || true
}
scribe_shapes_append() {
[ -n "$1" ] || { cat >/dev/null; return 0; }
mkdir -p "$(dirname "$1")" 2>/dev/null || true
awk '/^[a-z]+$/' >> "$1" 2>/dev/null || true
}
scribe_clear_session_ledgers() {
# SPARES `*.keep.ids`, which are evidence rather than exclusions — see
# `scribe_rules_append`. Everything else still goes: the convention is