docs(6204): shipped instruction text describes the product, not this instance's records
CI & Build / Python lint (push) Successful in 3s
CI & Build / Plugin hooks (push) Successful in 15s
CI & Build / TypeScript typecheck (push) Successful in 53s
CI & Build / integration (push) Successful in 1m10s
CI & Build / Python tests (push) Successful in 1m55s
CI & Build / Build & push image (push) Successful in 39s
CI & Build / Python lint (push) Successful in 3s
CI & Build / Plugin hooks (push) Successful in 15s
CI & Build / TypeScript typecheck (push) Successful in 53s
CI & Build / integration (push) Successful in 1m10s
CI & Build / Python tests (push) Successful in 1m55s
CI & Build / Build & push image (push) Successful in 39s
MCP tool docstrings and comments, the server's orientation notes, the plugin skills, every hook script and the plugin README/PACKAGING drop record ids, milestone numbers, dates and incident stories, and state each non-obvious why as a present-tense fact. History stays in the task logs. Corrections found on the way: - plugin README/PACKAGING said every hook is a GET; moment, rule-opened and reply-rules POST, so the plugin's key needs write scope. Both now say so, name Settings -> MCP Access, and list every wired hook and endpoint. - the contract-change hint printed "(lesson #4207)", this instance's record, to every install. It no longer cites a record. - tasks.py claimed there is no delete_task; there is. - rulebooks.py cited delete_event and a design spec that no longer exist. - scribe_after_write.sh carried an always-empty etag_q; removed. - test_write_path_trigger pinned "| tac | scribe_defs | head -1", text that existed only in a comment about the old code; it now pins the real tail -1 pipeline. - docs/development.md: CI runs on Gitea Actions. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
This commit is contained in:
@@ -7,12 +7,12 @@
|
||||
# only — never bodies; the agent calls get_note(id) to pull anything it judges
|
||||
# relevant. Most turns inject nothing.
|
||||
#
|
||||
# TWO ARMS SINCE #3852, on one request. Rules and preferences are retrieved
|
||||
# TWO ARMS, on one request. Rules and preferences are retrieved
|
||||
# against the same prompt and returned in the same payload, ahead of the notes
|
||||
# menu. That arm exists because the two act arms are keyed on a file write or
|
||||
# a command, so a rule governing what to SAY — extract intent from loose
|
||||
# phrasing, raise a conflict before acting, end a finding with an offer — had
|
||||
# no moment to fire at. The operator's message is the only query that exists
|
||||
# phrasing, raise a conflict before acting, end a finding with an offer —
|
||||
# would have no moment to fire at. The operator's message is the only query that exists
|
||||
# before a response is composed.
|
||||
#
|
||||
# The two arms are gated separately server-side: turning the notes menu off
|
||||
@@ -24,8 +24,8 @@
|
||||
# fails, the hook stays SILENT and exits 0 — it must never block a prompt.
|
||||
#
|
||||
# Config (same as scribe_session_context.sh), exported to the hook by Claude Code
|
||||
# with the userConfig key UPPERCASED (see #2198 — reading the lowercase spelling
|
||||
# silently disables this hook, and silence is indistinguishable from "nothing
|
||||
# with the userConfig key UPPERCASED (reading the lowercase spelling silently
|
||||
# disables this hook, and silence is indistinguishable from "nothing
|
||||
# cleared the threshold"):
|
||||
# CLAUDE_PLUGIN_OPTION_API_ENDPOINT base URL, no trailing slash
|
||||
# CLAUDE_PLUGIN_OPTION_API_TOKEN fmcp_ API key (sensitive)
|
||||
@@ -41,7 +41,7 @@ set -uo pipefail
|
||||
command -v curl >/dev/null 2>&1 || exit 0
|
||||
|
||||
# UserPromptSubmit delivers a JSON event on stdin: { prompt, session_id, cwd, ... }
|
||||
# Parsed ONCE into flat lines and then queried three times (#4107): a prompt is
|
||||
# Parsed ONCE into flat lines and then queried three times: a prompt is
|
||||
# the largest payload any hook reads, and re-parsing it per field is three
|
||||
# passes over the same text.
|
||||
event=$(cat 2>/dev/null || true)
|
||||
@@ -54,12 +54,12 @@ transcript=$(scribe_json_pick "$event_flat" '.transcript_path')
|
||||
# Nothing to retrieve against.
|
||||
[ -n "$prompt" ] || exit 0
|
||||
|
||||
# A turn CLAUDE CODE wrote, not the operator (#4200) — a task notification, a
|
||||
# A turn CLAUDE CODE wrote, not the operator — a task notification, a
|
||||
# slash-command echo, a local command's caveat banner. Leaving before the
|
||||
# request matters more for the LOG than for the noise: a retrieval call
|
||||
# recorded against a notification inflates this surface's denominator and
|
||||
# seeds `near_misses` with demand nobody expressed. scribe_skip_prompt carries
|
||||
# the measurement.
|
||||
# the reasoning.
|
||||
scribe_skip_prompt "$prompt" && exit 0
|
||||
|
||||
# Unconfigured install → silent (auto-inject is pure enrichment).
|
||||
@@ -67,20 +67,17 @@ scribe_config || exit 0
|
||||
|
||||
# Cap the query length — a giant prompt makes a giant URL for no extra signal.
|
||||
# `head -c`, not `cut -c1-2000`: cut is line-oriented and caps EACH LINE, so a
|
||||
# long multi-line prompt sailed past the budget entirely. Same defect as the
|
||||
# prior-art hook's code cap; this copy was missed when that one was fixed, and
|
||||
# scripts/check_plugin.py caught it.
|
||||
# long multi-line prompt would sail past the budget. scripts/check_plugin.py
|
||||
# checks every hook for this.
|
||||
q=$(printf '%s' "$prompt" | head -c 2000)
|
||||
# Encoded whole, never line by line. The predecessor here was `jq -rR`, which
|
||||
# reads a line at a time: a multi-line prompt came back as several separately
|
||||
# encoded lines joined by raw newlines and the request died. Single-line prompts
|
||||
# worked, which is why this looked healthy — the long, substantial prompts most
|
||||
# worth retrieving against were exactly the ones silently dropped. scribe_urlenc
|
||||
# reads bytes and has no notion of a line.
|
||||
# Encoded whole, never line by line: per-line encoding joins the pieces with
|
||||
# raw newlines, an invalid URL, and the long multi-line prompts most worth
|
||||
# retrieving against would be the ones dropped. scribe_urlenc reads bytes and
|
||||
# has no notion of a line.
|
||||
q_enc=$(printf '%s' "$q" | scribe_urlenc) || exit 0
|
||||
[ -n "$q_enc" ] || exit 0
|
||||
|
||||
# What the conversation is about, for a prompt too short to say (#4364). Sent
|
||||
# What the conversation is about, for a prompt too short to say. Sent
|
||||
# beside `q`, never folded into it: the server decides whether the prompt is
|
||||
# thin enough to need it, for the notes and rule arms alike. Capped at
|
||||
# 600 chars here as well as there, so the URL stays small whatever the reply.
|
||||
@@ -104,8 +101,8 @@ mkdir -p "$state_dir" 2>/dev/null || true
|
||||
# hooks already keep it under scribe-priorart — so that one session keeps ONE
|
||||
# list and a rule named here is not re-announced before the next Bash call.
|
||||
# A private copy here would make each arm's "already seen" mean something
|
||||
# different, which is the state #3749/#3750 exist to keep coherent. The
|
||||
# directory name is the prior-art hook's history, not a scope claim.
|
||||
# different. The directory is named for the prior-art hook, which is not a
|
||||
# scope claim.
|
||||
rule_state_dir="${TMPDIR:-/tmp}/scribe-priorart"
|
||||
mkdir -p "$rule_state_dir" 2>/dev/null || true
|
||||
idfile=""
|
||||
@@ -126,9 +123,9 @@ if [ -n "$session_id" ]; then
|
||||
# stops holding what it was told. scribe_rules_live carries the reasoning.
|
||||
rule_seen=$(scribe_rules_live "$rulefile")
|
||||
[ -n "$rule_seen" ] && exclude_q="${exclude_q}&exclude_rule_ids=${rule_seen}"
|
||||
# What the session actually OPENED, as against what it was shown (#4100).
|
||||
# What the session actually OPENED, as against what it was shown.
|
||||
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
|
||||
# Which reply shapes it already holds in full: 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")
|
||||
@@ -149,7 +146,7 @@ if [ -n "$idfile" ]; then
|
||||
scribe_json_list "$body_flat" '.note_ids' >> "$idfile" || true
|
||||
fi
|
||||
# Rules onto the SHARED ledger, stamped so they can age out. Only FRESH ids
|
||||
# come back in rule_ids (#3752) — a rule rendered as a repeat is already on
|
||||
# come back in rule_ids — a rule rendered as a repeat is already on
|
||||
# the ledger, and re-appending it would keep pushing its stamp forward so it
|
||||
# never aged at all.
|
||||
if [ -n "$rulefile" ]; then
|
||||
|
||||
Reference in New Issue
Block a user