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
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:
@@ -1,7 +1,7 @@
|
||||
{
|
||||
"name": "scribe",
|
||||
"description": "Scribe for Claude Code: connects the scribe MCP server, adds the hooks that deliver live project state and relevant records at the right moment, ships the shared client-neutral Scribe skills (using-scribe, writing-plans, reporting-back, systematic-debugging, verification, brainstorming, reusing-code, shape-accounting, family-canon), and syncs your saved Scribe Processes as skills (/scribe:sync).",
|
||||
"version": "2026.10.09.1835",
|
||||
"version": "2026.10.09.1845",
|
||||
"author": {
|
||||
"name": "Bryan Van Deusen"
|
||||
},
|
||||
|
||||
@@ -110,6 +110,7 @@ rule_state_dir="${TMPDIR:-/tmp}/scribe-priorart"
|
||||
mkdir -p "$rule_state_dir" 2>/dev/null || true
|
||||
idfile=""
|
||||
rulefile=""
|
||||
shapefile=""
|
||||
exclude_q=""
|
||||
if [ -n "$session_id" ]; then
|
||||
# session_id is an opaque token from Claude Code; keep only filename-safe chars.
|
||||
@@ -127,6 +128,11 @@ if [ -n "$session_id" ]; then
|
||||
[ -n "$rule_seen" ] && exclude_q="${exclude_q}&exclude_rule_ids=${rule_seen}"
|
||||
# What the session actually OPENED, as against what it was shown (#4100).
|
||||
exclude_q="${exclude_q}$(scribe_held_query "$rule_state_dir/${safe_sid}.opened.ids")"
|
||||
# Which reply shapes it already holds in full (milestone 500): the core is
|
||||
# sent whole once, and as its one-line reminder on every later turn.
|
||||
shapefile=$(scribe_shapes_file "$safe_sid")
|
||||
shapes_seen=$(scribe_shapes_seen "$shapefile")
|
||||
[ -n "$shapes_seen" ] && exclude_q="${exclude_q}&shapes_seen=${shapes_seen}"
|
||||
fi
|
||||
|
||||
body=$(curl -fsS --max-time 5 \
|
||||
@@ -150,6 +156,9 @@ if [ -n "$rulefile" ]; then
|
||||
scribe_json_list "$body_flat" '.rule_ids' \
|
||||
| scribe_rules_append "$rulefile"
|
||||
fi
|
||||
if [ -n "$shapefile" ]; then
|
||||
scribe_json_list "$body_flat" '.shape_keys' | scribe_shapes_append "$shapefile"
|
||||
fi
|
||||
|
||||
scribe_json_out UserPromptSubmit "$context"
|
||||
exit 0
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -122,6 +122,11 @@ ledger_q=""
|
||||
rule_seen=$(scribe_rules_live "$rulefile")
|
||||
[ -n "$rule_seen" ] && ledger_q="&exclude_rule_ids=${rule_seen}"
|
||||
ledger_q="${ledger_q}$(scribe_held_query "$state_dir/${safe_sid}.opened.ids")"
|
||||
# The reply-shape ledger (milestone 500): a shape this session already holds in
|
||||
# full comes back as its reminder.
|
||||
shapefile=$(scribe_shapes_file "$safe_sid")
|
||||
shapes_seen=$(scribe_shapes_seen "$shapefile")
|
||||
[ -n "$shapes_seen" ] && ledger_q="${ledger_q}&shapes_seen=${shapes_seen}"
|
||||
|
||||
# The event whole, since which field a mapping matches is the mapping's
|
||||
# business. A write of a very large file is reduced to its name: the moments a
|
||||
@@ -141,6 +146,7 @@ body=$(printf '%s' "$event" | curl -fsS --max-time 3 \
|
||||
|
||||
body_flat=$(printf '%s' "$body" | scribe_json_flat)
|
||||
scribe_json_list "$body_flat" '.rule_ids' | scribe_rules_append "$rulefile"
|
||||
scribe_json_list "$body_flat" '.shape_keys' | scribe_shapes_append "$shapefile"
|
||||
|
||||
context=$(scribe_json_pick "$body_flat" '.context')
|
||||
[ -n "$context" ] || exit 0
|
||||
|
||||
Reference in New Issue
Block a user