@rulemetric/local 0.7.41 → 0.7.43

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.
@@ -0,0 +1,52 @@
1
+ -- Subtractive suggestions: give instruction_suggestions a direction.
2
+ --
3
+ -- The pipeline has only ever been able to say "add this". The removal engine
4
+ -- already exists (apps/api/src/routes/insights/grooming.ts: never-used +
5
+ -- measured-harmful, behind the capture-health gate and the insurance-rule
6
+ -- allowlist) but it terminates in a web-only endpoint. Meanwhile the additive
7
+ -- feed owns the high-traffic channel — the SessionStart hook injects it into
8
+ -- every session — and sits at 0 accepted out of 335.
9
+ --
10
+ -- `kind` lets both directions share one row shape, one accept/dismiss
11
+ -- lifecycle, and one events funnel, so acceptance rate becomes comparable
12
+ -- across directions. That comparison is the point: it tells us whether 0/335
13
+ -- is a suggestion-quality problem or an add-vs-remove problem.
14
+ --
15
+ -- add — adopt a catalog/personal instruction not currently used here
16
+ -- remove — retire an own instruction (never-used, or measured-harmful)
17
+ -- trim — keep it, but it costs tokens and measures neutral (slice 4)
18
+
19
+ ALTER TABLE instruction_suggestions
20
+ ADD COLUMN IF NOT EXISTS kind text NOT NULL DEFAULT 'add';
21
+
22
+ ALTER TABLE instruction_suggestions
23
+ DROP CONSTRAINT IF EXISTS instruction_suggestions_kind_check;
24
+
25
+ ALTER TABLE instruction_suggestions
26
+ ADD CONSTRAINT instruction_suggestions_kind_check
27
+ CHECK (kind IN ('add', 'remove', 'trim'));
28
+
29
+ -- The unique index MUST carry `kind`, and this is not cosmetic. The upsert
30
+ -- conflict target is (user_id, project_path, instruction_id). Without `kind`,
31
+ -- a later remove-suggestion for an instruction the user had already ACCEPTED
32
+ -- as an add would collide with that accepted row and overwrite its score and
33
+ -- reason in place — silently converting an accepted adoption into a retirement
34
+ -- proposal, and corrupting the very acceptance funnel this column exists to
35
+ -- measure. Widening the index makes the two directions independent rows.
36
+ -- Create the wider index BEFORE dropping the narrower one. The reverse order
37
+ -- leaves a window with no uniqueness guarantee at all, and the suggestion worker
38
+ -- upserts on a cron — a concurrent batch landing in that window could insert the
39
+ -- duplicate the index exists to prevent.
40
+ CREATE UNIQUE INDEX IF NOT EXISTS idx_instruction_suggestions_user_project_instruction_kind
41
+ ON instruction_suggestions (user_id, project_path, instruction_id, kind);
42
+
43
+ DROP INDEX IF EXISTS idx_instruction_suggestions_user_project_instruction;
44
+
45
+ -- Pending-by-direction reads (the feed filters on kind, and the funnel groups
46
+ -- by it) would otherwise fall back to the (user_id, project_path) index and
47
+ -- re-filter in the heap.
48
+ CREATE INDEX IF NOT EXISTS idx_instruction_suggestions_user_kind
49
+ ON instruction_suggestions (user_id, kind);
50
+
51
+ COMMENT ON COLUMN instruction_suggestions.kind IS
52
+ 'Direction of the proposal: add (adopt), remove (retire), trim (reduce). Default add — every pre-existing row is an adoption proposal.';