@rulemetric/local 0.10.0 → 0.11.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.
@@ -0,0 +1,113 @@
1
+ -- 00184 — the auto-accept budget becomes a setting, and the producer stops
2
+ -- starving the source it was built to serve.
3
+ --
4
+ -- Three facts measured on 2026-08-12, all from the same project:
5
+ --
6
+ -- 1. The per-window budget was counted by RAW project_path string equality.
7
+ -- `/Code/agents/momento-mori` and `/Code/agents/momento-mori/apps/cli`
8
+ -- are one repo and resolve to ONE projects row, but each drew its own
9
+ -- allowance and each hit exactly 5/5 in the same 7 days — the repo
10
+ -- adopted 10 rules under a policy that intends 5. Any repo whose
11
+ -- sessions start in a subdirectory has as many budgets as it has
12
+ -- subdirectories. Fixed in auto-accept-eligibility.ts by counting
13
+ -- through instruction_suggestions.project_id; nothing to do here.
14
+ --
15
+ -- 2. The budget was a compile-time constant, so raising it for one noisy
16
+ -- repo meant a deploy. It is now a setting: NULL columns here mean "use
17
+ -- the shipped default", per-project overrides live in
18
+ -- projects.metadata.autoAcceptLimits and win over these.
19
+ --
20
+ -- 3. THIS producer only ever queued a run when the project held a
21
+ -- suggestion scoring >= 0.8. But GET /api/instruction-suggestions/
22
+ -- auto-acceptable deliberately exempts insights-authored rows from the
23
+ -- score gate (they are filed at 0.5 by the nomination endpoint). So in a
24
+ -- project whose only proposals came from insights, the authoritative
25
+ -- gate said "eligible" and this function never queued the job that would
26
+ -- have asked it. Every insights nomination in such a project was
27
+ -- unreachable — not declined, not surfaced, just never considered.
28
+
29
+ -- ── Settable budget ─────────────────────────────────────────────────────────
30
+ alter table public.profiles
31
+ add column if not exists auto_accept_min_score real,
32
+ add column if not exists auto_accept_max_per_run integer,
33
+ add column if not exists auto_accept_max_per_window integer,
34
+ add column if not exists auto_accept_window_days integer;
35
+
36
+ comment on column public.profiles.auto_accept_min_score is
37
+ 'Account override for the auto-accept score gate. NULL = shipped default (0.8). Bounded 0..1 by the API on read AND write — a settable brake still has to be a brake.';
38
+ comment on column public.profiles.auto_accept_max_per_run is
39
+ 'Account override for auto-accepts per nightly run per project. NULL = shipped default (3). Bounded 0..25.';
40
+ comment on column public.profiles.auto_accept_max_per_window is
41
+ 'Account override for auto-accepts per project per window. NULL = shipped default (15). Bounded 0..100. The brake on CLAUDE.md bloat: an over-large CLAUDE.md measured -22.1% (stratified, significant).';
42
+ comment on column public.profiles.auto_accept_window_days is
43
+ 'Account override for the budget window. NULL = shipped default (7). Bounded 1..90.';
44
+
45
+ -- ── Producer ────────────────────────────────────────────────────────────────
46
+ -- Same cheap-gates-only contract as 00170: this decides whether to ASK, never
47
+ -- whether to adopt. The authoritative check stays server-side.
48
+ create or replace function public.enqueue_auto_accept_suggestions()
49
+ returns integer
50
+ language plpgsql
51
+ security definer
52
+ set search_path = public
53
+ as $$
54
+ declare
55
+ queued integer;
56
+ begin
57
+ with eligible as (
58
+ select s.user_id, s.project_path
59
+ from instruction_suggestions s
60
+ join profiles p on p.id = s.user_id
61
+ join instructions i on i.id = s.instruction_id
62
+ left join projects proj on proj.id = s.project_id
63
+ where
64
+ s.kind = 'add'
65
+ -- Score OR provenance. Insights nominations are filed at 0.5 by
66
+ -- POST /api/insights/recommendations/nominate and exempted from the
67
+ -- score gate by the authoritative endpoint; requiring 0.8 here made
68
+ -- that exemption unreachable in any project without a high-scoring
69
+ -- catalog proposal. The per-window budget still bounds what a run may
70
+ -- take, so a permissive trigger cannot become a permissive adoption.
71
+ and (s.score >= 0.8 or i.frontmatter->>'source' = 'insights')
72
+ -- Instruction bodies only, mirroring the endpoint: a skill pasted into
73
+ -- CLAUDE.md is a category error, and queueing a job that can only refuse
74
+ -- is how the loop reported "nothing eligible" for three days.
75
+ and i.type = 'instruction'
76
+ and i.archived = false
77
+ and s.accepted_at is null
78
+ and s.dismissed_at is null
79
+ and s.declined_at is null
80
+ and s.is_stale = false
81
+ -- Consent: per-project override wins, else the user default (true).
82
+ and coalesce(
83
+ proj.metadata->>'autoAcceptSuggestions',
84
+ p.auto_accept_suggestions::text
85
+ ) = 'true'
86
+ -- One in flight per (user, project), per the 00083/00086 pattern.
87
+ and not exists (
88
+ select 1 from agent_jobs j
89
+ where j.task_kind = 'cron_auto_accept_suggestions'
90
+ and j.status in ('pending', 'claimed', 'running')
91
+ and j.payload->>'projectPath' = s.project_path
92
+ and j.user_id = s.user_id
93
+ )
94
+ group by s.user_id, s.project_path
95
+ ),
96
+ ins as (
97
+ insert into agent_jobs (user_id, task_kind, status, payload, dedupe_key)
98
+ select e.user_id,
99
+ 'cron_auto_accept_suggestions',
100
+ 'pending',
101
+ jsonb_build_object('projectPath', e.project_path),
102
+ 'auto-accept:' || e.user_id::text || ':' || e.project_path
103
+ from eligible e
104
+ returning 1
105
+ )
106
+ select count(*)::int into queued from ins;
107
+
108
+ return queued;
109
+ end;
110
+ $$;
111
+
112
+ comment on function public.enqueue_auto_accept_suggestions() is
113
+ 'Nightly producer for cron_auto_accept_suggestions. One row per (user, project) holding at least one consented ADD proposal that is either high-confidence (>= 0.8) or insights-authored, and whose instruction is a non-archived instruction body. Cheap gates only — the authoritative eligibility check is GET /api/instruction-suggestions/auto-acceptable, which also applies the per-project budget. Budget overrides: profiles.auto_accept_max_per_window (account) and projects.metadata.autoAcceptLimits (project). Turn it off for one repo: update projects set metadata = coalesce(metadata,''{}''::jsonb) || ''{"autoAcceptSuggestions":false}''::jsonb where id = ...; or globally: update profiles set auto_accept_suggestions = false where id = ...';