@rulemetric/hooks 0.7.44 → 0.8.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/package.json +1 -1
- package/scripts/session-start.sh +59 -34
package/package.json
CHANGED
package/scripts/session-start.sh
CHANGED
|
@@ -136,49 +136,64 @@ _rulemetric_suggest_instructions() {
|
|
|
136
136
|
# week of grooming would evict every adoption proposal and the additive feed
|
|
137
137
|
# would silently go to zero. Over-fetch, then cap each direction separately
|
|
138
138
|
# below so neither can starve the other.
|
|
139
|
+
# renderFor=session-start asks the API for the RENDER SET — already capped per
|
|
140
|
+
# direction, already ordered adopt-first, with counts that describe itself. The
|
|
141
|
+
# caps used to live in this script as jq slices, which meant the server could
|
|
142
|
+
# not tell a row it showed from a row it merely sent: `surfaced` events were
|
|
143
|
+
# logged for rows the caps never displayed, inflating the denominator of the
|
|
144
|
+
# acceptance rate this feed exists to measure.
|
|
145
|
+
#
|
|
146
|
+
# groupByKind is still sent so an API that predates renderFor degrades to the
|
|
147
|
+
# old mixed-pool response rather than to a score-ordered page that buries the
|
|
148
|
+
# subtractive directions.
|
|
149
|
+
#
|
|
150
|
+
# sessionId carries HOOK_SESSION_ID so the server can assign this session an
|
|
151
|
+
# ARM for any instruction under test — inject it, or deliberately withhold it —
|
|
152
|
+
# and record that decision BEFORE deciding what to return. The hook stays a
|
|
153
|
+
# dumb renderer: it never learns which arm it is in, and must not, or the
|
|
154
|
+
# decision would be re-derivable client-side and therefore forgeable.
|
|
155
|
+
#
|
|
156
|
+
# An API that predates arms ignores the parameter and returns the same render
|
|
157
|
+
# set as before, so an older server degrades to "always inject" rather than to
|
|
158
|
+
# a silently empty feed.
|
|
139
159
|
response="$(curl -s --noproxy '*' --max-time 4 \
|
|
140
|
-
"${api_url}/api/instruction-suggestions?projectPath=${encoded_path}&limit=10&includeContent=true&groupByKind=true" \
|
|
160
|
+
"${api_url}/api/instruction-suggestions?projectPath=${encoded_path}&limit=10&includeContent=true&groupByKind=true&renderFor=session-start&sessionId=${HOOK_SESSION_ID}" \
|
|
141
161
|
-H "Authorization: Bearer ${auth_token}" 2>/dev/null)" || return 0
|
|
142
162
|
|
|
143
163
|
local count
|
|
144
164
|
count="$(printf '%s' "$response" | jq -r '.suggestions | length' 2>/dev/null)" || return 0
|
|
145
165
|
[ "$count" != "0" ] && [ "$count" != "null" ] && [ -n "$count" ] || return 0
|
|
146
166
|
|
|
147
|
-
|
|
148
|
-
|
|
149
|
-
|
|
150
|
-
|
|
151
|
-
|
|
152
|
-
|
|
153
|
-
'
|
|
154
|
-
|
|
155
|
-
|
|
156
|
-
|
|
167
|
+
local add_count cost_count high_count render_idx
|
|
168
|
+
add_count="$(printf '%s' "$response" | jq -r '.render.addCount // empty' 2>/dev/null)" || add_count=""
|
|
169
|
+
|
|
170
|
+
if [ -n "$add_count" ]; then
|
|
171
|
+
# Render-set response: print what we were given, in the order given.
|
|
172
|
+
cost_count="$(printf '%s' "$response" | jq -r '.render.costCount // 0' 2>/dev/null)" || cost_count=0
|
|
173
|
+
high_count="$(printf '%s' "$response" | jq -r '.render.highConfidenceCount // 0' 2>/dev/null)" || high_count=0
|
|
174
|
+
render_idx="$(seq 0 "$((count - 1))" 2>/dev/null)" || return 0
|
|
175
|
+
else
|
|
176
|
+
# COMPATIBILITY SHIM — an API older than renderFor returned a mixed pool, so
|
|
177
|
+
# the caps have to be applied here. This is a second copy of a policy that
|
|
178
|
+
# now lives on the server, and a forked policy is one that eventually drifts:
|
|
179
|
+
# delete this branch once the minimum supported API understands renderFor.
|
|
180
|
+
local add_idx rem_idx trim_idx rem_count trim_count
|
|
181
|
+
add_idx="$(printf '%s' "$response" | jq -r \
|
|
182
|
+
'[.suggestions | to_entries[] | select((.value.kind // "add") == "add") | .key][0:3] | .[]' 2>/dev/null)" || add_idx=""
|
|
183
|
+
rem_idx="$(printf '%s' "$response" | jq -r \
|
|
184
|
+
'[.suggestions | to_entries[] | select(.value.kind == "remove") | .key][0:2] | .[]' 2>/dev/null)" || rem_idx=""
|
|
185
|
+
trim_idx="$(printf '%s' "$response" | jq -r \
|
|
186
|
+
'[.suggestions | to_entries[] | select(.value.kind == "trim") | .key][0:1] | .[]' 2>/dev/null)" || trim_idx=""
|
|
187
|
+
render_idx="$(printf '%s\n%s\n%s\n' "$add_idx" "$rem_idx" "$trim_idx" | grep -v '^$' || true)"
|
|
188
|
+
high_count="$(printf '%s' "$response" | jq -r \
|
|
189
|
+
'[[.suggestions | to_entries[] | select((.value.kind // "add") == "add")][0:3][] | select(.value.content != null)] | length' 2>/dev/null)" || high_count="0"
|
|
190
|
+
rem_count="$(printf '%s\n' "$rem_idx" | grep -c '[0-9]' 2>/dev/null)" || rem_count=0
|
|
191
|
+
trim_count="$(printf '%s\n' "$trim_idx" | grep -c '[0-9]' 2>/dev/null)" || trim_count=0
|
|
192
|
+
cost_count=$((rem_count + trim_count))
|
|
193
|
+
add_count="$(printf '%s\n' "$add_idx" | grep -c '[0-9]' 2>/dev/null)" || add_count=0
|
|
194
|
+
fi
|
|
157
195
|
[ -n "$render_idx" ] || return 0
|
|
158
196
|
|
|
159
|
-
# Count high-confidence rows among the ADDS WE WILL ACTUALLY RENDER. Counting
|
|
160
|
-
# the whole response over-counts now that the fetch pulls 6 to cap 3+2: the
|
|
161
|
-
# header announced "5 high-confidence instruction(s) included below" with 3
|
|
162
|
-
# below it. A header that miscounts what follows it is how a reader stops
|
|
163
|
-
# trusting the block.
|
|
164
|
-
local high_count
|
|
165
|
-
high_count="$(printf '%s' "$response" | jq -r \
|
|
166
|
-
'[[.suggestions | to_entries[] | select((.value.kind // "add") == "add")][0:3][] | select(.value.content != null)] | length' 2>/dev/null)" || high_count="0"
|
|
167
|
-
# ONE count for the shared "costing you" header. Counting only removals would
|
|
168
|
-
# under-report the moment a trim renders beneath the same heading — the same
|
|
169
|
-
# header-miscounts-its-own-list bug just fixed above, reintroduced by a third
|
|
170
|
-
# direction.
|
|
171
|
-
local rem_count trim_count cost_count
|
|
172
|
-
rem_count="$(printf '%s\n' "$rem_idx" | grep -c '[0-9]' 2>/dev/null)" || rem_count=0
|
|
173
|
-
trim_count="$(printf '%s\n' "$trim_idx" | grep -c '[0-9]' 2>/dev/null)" || trim_count=0
|
|
174
|
-
cost_count=$((rem_count + trim_count))
|
|
175
|
-
|
|
176
|
-
# The header describes the ADDITIVE rows only. `count` is now a mixed total, so
|
|
177
|
-
# using it here would announce "N instructions may help with this project" and
|
|
178
|
-
# then list retirements underneath it.
|
|
179
|
-
local add_count
|
|
180
|
-
add_count="$(printf '%s\n' "$add_idx" | grep -c '[0-9]' 2>/dev/null)" || add_count=0
|
|
181
|
-
|
|
182
197
|
printf '\n---\n'
|
|
183
198
|
if [ "$high_count" != "0" ] && [ -n "$high_count" ]; then
|
|
184
199
|
printf '[rulemetric] %s high-confidence instruction(s) included below as session context.\n' "$high_count"
|
|
@@ -253,6 +268,11 @@ _rulemetric_suggest_instructions() {
|
|
|
253
268
|
# reader (human or model) could act without going to look an id up. With
|
|
254
269
|
# several suggestions on screen one shared CTA is ambiguous even once it
|
|
255
270
|
# carries an id, so the command belongs to the row it accepts.
|
|
271
|
+
#
|
|
272
|
+
# A `graduated:`-prefixed id is a RULE, not a proposal (ticket 04): its
|
|
273
|
+
# question is settled by measurement, so there is nothing to accept and the
|
|
274
|
+
# CTA would be an instruction to act on a decision already made.
|
|
275
|
+
case "$sid" in graduated:*|experiment:*) continue ;; esac
|
|
256
276
|
if [ -n "$sid" ] && [ "$sid" != "null" ]; then
|
|
257
277
|
printf ' Accept: rulemetric suggestions accept %s\n' "$sid"
|
|
258
278
|
fi
|
|
@@ -270,6 +290,11 @@ _rulemetric_suggest_instructions() {
|
|
|
270
290
|
for i in $render_idx; do
|
|
271
291
|
local sid has_content event_type
|
|
272
292
|
sid="$(printf '%s' "$response" | jq -r ".suggestions[$i].id" 2>/dev/null)" || break
|
|
293
|
+
# A graduated rule (ticket 04) has no suggestion row — its id is synthetic
|
|
294
|
+
# — and its surfacing is not a funnel event: the budget and acceptance
|
|
295
|
+
# metrics both measure QUESTIONS, and a rule is an answer. Posting would
|
|
296
|
+
# 404 anyway; skipping keeps the log clean and the denominators honest.
|
|
297
|
+
case "$sid" in graduated:*|experiment:*) continue ;; esac
|
|
273
298
|
has_content="$(printf '%s' "$response" | jq -r ".suggestions[$i].content // \"null\"" 2>/dev/null)" || has_content="null"
|
|
274
299
|
if [ "$has_content" != "null" ] && [ -n "$has_content" ]; then
|
|
275
300
|
event_type="surfaced_with_content"
|