feat(500): Settings shows the shipped reply shapes, what adjusts each, and how often each arrives
CI & Build / Python lint (push) Successful in 3s
CI & Build / Plugin hooks (push) Successful in 16s
CI & Build / TypeScript typecheck (push) Successful in 57s
CI & Build / integration (push) Successful in 1m19s
CI & Build / Python tests (push) Successful in 1m58s
CI & Build / Build & push image (push) Successful in 39s

Settings > General > Reply shapes. One menu row per shape (core, completion,
asks, plan): its title and when it arrives, how many of the operator's
preferences adjust it, and its deliveries over 30 days (in full / as a
reminder, "—" when the counts cannot be read). A verdict line leads. Opening a
row shows the shipped text, read-only because it is product, and the
preferences mounted on its moment; each opens in the rule editor, and "Adjust
this" opens a new preference already mounted on the shape's moment with a
starting trigger.

The rule editor takes an optional preset (kind, moments, trigger) and, opened
outside the Rules view, asks where a new record lives instead of dropping it.
GET /api/retrieval/reply-shapes now returns reply_shapes.overview: the
catalog plus mounted preferences (the same lookup that delivers them) and
counts from the reply_shape delivery rows. #5497 (step 5 of milestone 500).

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
This commit is contained in:
2026-10-09 16:19:45 -04:00
co-authored by Claude Opus 5.5
parent b00dc7c4c2
commit 69dddb7551
8 changed files with 505 additions and 16 deletions
+47
View File
@@ -0,0 +1,47 @@
import { apiGet } from "@/api/client";
/**
* The reply shapes Scribe ships (milestone 500) — `GET /api/retrieval/reply-shapes`.
* The same catalog the `list_reply_shapes` MCP tool returns, plus what only
* the operator's view needs: the preferences mounted on each shape's moment,
* and how often each shape was delivered.
*/
export interface ShapePreference {
id: number;
title: string;
statement: string;
}
/** How often a shape went out: whole, or as its one-line reminder. */
export interface ShapeDeliveries {
full: number;
pointer: number;
}
export interface ReplyShape {
key: string;
title: string;
/** The moment it rides — a preference mounted here adjusts it. */
moment: string;
/** What is happening at that moment. */
means: string;
/** When the shape arrives, in words. */
delivered: string;
/** The shipped text. Product, so read-only. */
text: string;
preferences: ShapePreference[];
/** null when the counts could not be read — unknown, not zero. */
deliveries: ShapeDeliveries | null;
}
export interface ReplyShapesOverview {
shapes: ReplyShape[];
total: number;
days: number;
deliveries_failed: boolean;
}
export async function getReplyShapes(days?: number): Promise<ReplyShapesOverview> {
const q = days ? `?days=${days}` : "";
return apiGet<ReplyShapesOverview>(`/api/retrieval/reply-shapes${q}`);
}
+9
View File
@@ -54,6 +54,15 @@ export interface RulebookTopic {
*/
export type RuleKind = "rule" | "preference";
/** A new record opened from somewhere that already knows what it is for —
* "Adjust this" on a reply shape opens a preference mounted on that shape's
* moment (milestone 500 step 5). The editor applies it once, when creating. */
export interface RulePreset {
kind?: RuleKind;
moments?: string[];
whenToApply?: string;
}
export interface Rule {
id: number;
topic_id: number | null;