@automagik/genie 4.260429.10 → 4.260429.12

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 CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@automagik/genie",
3
- "version": "4.260429.10",
3
+ "version": "4.260429.12",
4
4
  "description": "Collaborative terminal toolkit for human + AI workflows. NOTE: the npm distribution is being soft-deprecated — the canonical install is `curl -fsSL https://get.automagik.dev/genie | bash` (cosign + SLSA verified). See https://automagik.dev/genie/security/distribution-sovereignty",
5
5
  "type": "module",
6
6
  "bin": {
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "genie",
3
- "version": "4.260429.10",
3
+ "version": "4.260429.12",
4
4
  "description": "Human-AI partnership for Claude Code. Share a terminal, orchestrate workers, evolve together. Brainstorm ideas, turn them into wishes, execute with /work, validate with /review, and ship as one team.",
5
5
  "author": {
6
6
  "name": "Namastex Labs"
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "genie-plugin",
3
- "version": "4.260429.10",
3
+ "version": "4.260429.12",
4
4
  "private": true,
5
5
  "description": "Runtime dependencies for genie bundled CLIs",
6
6
  "type": "module",
@@ -0,0 +1,63 @@
1
+ -- 057_hook_perf_baseline_view_filter_fix.sql — fix #1494 residual
2
+ --
3
+ -- Background: #1492 (the schema fix) accepts the new `event` key emitted by
4
+ -- runHandler post-#1485, so hook.delivery spans now insert into
5
+ -- genie_runtime_events without rejection. But dog-fooder-11eb's verdict
6
+ -- (state/evidence/followups-1490-1491-1493/REPORT.md) flagged a SECOND
7
+ -- bug: the hook_perf_baseline view created by migration 056 filters
8
+ -- `WHERE kind = 'hook.delivery'`, but the actual emitted row carries:
9
+ --
10
+ -- subject = 'hook.delivery' ← what we want to filter on
11
+ -- kind = 'system' ← what the view incorrectly filters on
12
+ -- data->>'_kind' = 'span' ← span marker
13
+ --
14
+ -- Net effect: rows accepted into the table never appear in the view, so
15
+ -- `genie doctor --perf` and `hook_perf_baseline` queries always returned
16
+ -- empty. The hookify-perf-foundation telemetry value (#1485) stayed inert.
17
+ --
18
+ -- Fix: replace the view to filter on `subject='hook.delivery'` AND require
19
+ -- `data->>'_kind' = 'span'` (so we only count completed spans, not other
20
+ -- runtime events that might happen to share the subject).
21
+
22
+ CREATE OR REPLACE VIEW hook_perf_baseline AS
23
+ WITH spans AS (
24
+ SELECT
25
+ COALESCE(data->>'event', '<unknown>') AS event_name,
26
+ COALESCE(data->>'tool', '<none>') AS tool_name,
27
+ COALESCE(data->>'hook_name', '<unknown>') AS handler_name,
28
+ NULLIF(data->>'duration_ms', '')::numeric AS duration_ms,
29
+ created_at
30
+ FROM genie_runtime_events
31
+ WHERE subject = 'hook.delivery'
32
+ AND data->>'_kind' = 'span'
33
+ AND data ? 'duration_ms'
34
+ )
35
+ SELECT
36
+ event_name,
37
+ tool_name,
38
+ handler_name,
39
+ PERCENTILE_CONT(0.5)
40
+ WITHIN GROUP (ORDER BY duration_ms)
41
+ FILTER (WHERE created_at >= now() - interval '1 hour') AS p50_1h,
42
+ PERCENTILE_CONT(0.99)
43
+ WITHIN GROUP (ORDER BY duration_ms)
44
+ FILTER (WHERE created_at >= now() - interval '1 hour') AS p99_1h,
45
+ PERCENTILE_CONT(0.5)
46
+ WITHIN GROUP (ORDER BY duration_ms)
47
+ FILTER (WHERE created_at >= now() - interval '24 hours') AS p50_24h,
48
+ PERCENTILE_CONT(0.99)
49
+ WITHIN GROUP (ORDER BY duration_ms)
50
+ FILTER (WHERE created_at >= now() - interval '24 hours') AS p99_24h,
51
+ PERCENTILE_CONT(0.5)
52
+ WITHIN GROUP (ORDER BY duration_ms)
53
+ FILTER (WHERE created_at >= now() - interval '7 days') AS p50_7d,
54
+ PERCENTILE_CONT(0.99)
55
+ WITHIN GROUP (ORDER BY duration_ms)
56
+ FILTER (WHERE created_at >= now() - interval '7 days') AS p99_7d,
57
+ COUNT(*) FILTER (WHERE created_at >= now() - interval '24 hours')::bigint AS sample_count_24h
58
+ FROM spans
59
+ WHERE duration_ms IS NOT NULL
60
+ GROUP BY event_name, tool_name, handler_name;
61
+
62
+ COMMENT ON VIEW hook_perf_baseline IS
63
+ 'Rolling P50/P99 per (event, tool, handler) over 1h/24h/7d windows. Source: hook.delivery spans in genie_runtime_events (filtered by subject + data._kind=span). Fixed in #1494 residual / migration 057 — original migration 056 filtered the wrong column.';
@@ -48,7 +48,7 @@ describe.skipIf(!DB_AVAILABLE)('hook_perf_baseline view', () => {
48
48
  test('empty source data → view returns no rows (no error)', async () => {
49
49
  const sql = await getConnection();
50
50
  // Wipe any rows the migration runner might have left.
51
- await sql`DELETE FROM genie_runtime_events WHERE kind = 'hook.delivery'`;
51
+ await sql`DELETE FROM genie_runtime_events WHERE subject = 'hook.delivery' OR kind = 'hook.delivery'`;
52
52
  const rows = (await sql`SELECT * FROM hook_perf_baseline`) as unknown[];
53
53
  expect(rows.length).toBe(0);
54
54
  });
@@ -58,11 +58,12 @@ describe.skipIf(!DB_AVAILABLE)('hook_perf_baseline view', () => {
58
58
  // Wipe and seed a known distribution: durations 1..101 ms for one
59
59
  // (event, tool, handler) tuple. Created 5 minutes ago so they fall
60
60
  // inside the 1h, 24h, AND 7d windows.
61
- await sql`DELETE FROM genie_runtime_events WHERE kind = 'hook.delivery'`;
61
+ await sql`DELETE FROM genie_runtime_events WHERE subject = 'hook.delivery' OR kind = 'hook.delivery'`;
62
62
 
63
63
  const fiveMinAgo = new Date(Date.now() - 5 * 60_000);
64
64
  for (let i = 1; i <= 101; i++) {
65
65
  const data = sql.json({
66
+ _kind: 'span',
66
67
  event: 'PreToolUse',
67
68
  tool: 'Bash',
68
69
  hook_name: 'branch-guard',
@@ -70,9 +71,9 @@ describe.skipIf(!DB_AVAILABLE)('hook_perf_baseline view', () => {
70
71
  });
71
72
  await sql`
72
73
  INSERT INTO genie_runtime_events
73
- (repo_path, kind, source, agent, text, data, created_at)
74
+ (repo_path, subject, kind, source, agent, text, data, created_at)
74
75
  VALUES
75
- ('test', 'hook.delivery', 'hooks', 'test', '', ${data}, ${fiveMinAgo})
76
+ ('test', 'hook.delivery', 'system', 'hooks', 'test', '', ${data}, ${fiveMinAgo})
76
77
  `;
77
78
  }
78
79
 
@@ -120,7 +121,7 @@ describe.skipIf(!DB_AVAILABLE)('hook_perf_baseline view', () => {
120
121
 
121
122
  test('rolling windows correctly partition rows by created_at', async () => {
122
123
  const sql = await getConnection();
123
- await sql`DELETE FROM genie_runtime_events WHERE kind = 'hook.delivery'`;
124
+ await sql`DELETE FROM genie_runtime_events WHERE subject = 'hook.delivery' OR kind = 'hook.delivery'`;
124
125
 
125
126
  // Two cohorts:
126
127
  // - 50 rows at 30 minutes ago, duration 10ms → falls in 1h, 24h, 7d
@@ -130,6 +131,7 @@ describe.skipIf(!DB_AVAILABLE)('hook_perf_baseline view', () => {
130
131
 
131
132
  for (let i = 0; i < 50; i++) {
132
133
  const data = sql.json({
134
+ _kind: 'span',
133
135
  event: 'PreToolUse',
134
136
  tool: 'Read',
135
137
  hook_name: 'freshness',
@@ -137,13 +139,14 @@ describe.skipIf(!DB_AVAILABLE)('hook_perf_baseline view', () => {
137
139
  });
138
140
  await sql`
139
141
  INSERT INTO genie_runtime_events
140
- (repo_path, kind, source, agent, text, data, created_at)
142
+ (repo_path, subject, kind, source, agent, text, data, created_at)
141
143
  VALUES
142
- ('test', 'hook.delivery', 'hooks', 'test', '', ${data}, ${thirtyMinAgo})
144
+ ('test', 'hook.delivery', 'system', 'hooks', 'test', '', ${data}, ${thirtyMinAgo})
143
145
  `;
144
146
  }
145
147
  for (let i = 0; i < 50; i++) {
146
148
  const data = sql.json({
149
+ _kind: 'span',
147
150
  event: 'PreToolUse',
148
151
  tool: 'Read',
149
152
  hook_name: 'freshness',
@@ -151,9 +154,9 @@ describe.skipIf(!DB_AVAILABLE)('hook_perf_baseline view', () => {
151
154
  });
152
155
  await sql`
153
156
  INSERT INTO genie_runtime_events
154
- (repo_path, kind, source, agent, text, data, created_at)
157
+ (repo_path, subject, kind, source, agent, text, data, created_at)
155
158
  VALUES
156
- ('test', 'hook.delivery', 'hooks', 'test', '', ${data}, ${fiveDaysAgo})
159
+ ('test', 'hook.delivery', 'system', 'hooks', 'test', '', ${data}, ${fiveDaysAgo})
157
160
  `;
158
161
  }
159
162
 
@@ -186,27 +189,29 @@ describe.skipIf(!DB_AVAILABLE)('hook_perf_baseline view', () => {
186
189
 
187
190
  test('rows missing data.duration_ms are filtered out', async () => {
188
191
  const sql = await getConnection();
189
- await sql`DELETE FROM genie_runtime_events WHERE kind = 'hook.delivery'`;
192
+ await sql`DELETE FROM genie_runtime_events WHERE subject = 'hook.delivery' OR kind = 'hook.delivery'`;
190
193
 
191
194
  // One valid row + one row missing duration_ms.
192
195
  const fiveMinAgo = new Date(Date.now() - 5 * 60_000);
193
196
  const validData = sql.json({
197
+ _kind: 'span',
194
198
  event: 'Stop',
195
199
  tool: '<none>',
196
200
  hook_name: 'runtime-emit-assistant-response',
197
201
  duration_ms: 42,
198
202
  });
199
203
  const missingDurData = sql.json({
204
+ _kind: 'span',
200
205
  event: 'Stop',
201
206
  tool: '<none>',
202
207
  hook_name: 'runtime-emit-assistant-response',
203
208
  });
204
209
  await sql`
205
210
  INSERT INTO genie_runtime_events
206
- (repo_path, kind, source, agent, text, data, created_at)
211
+ (repo_path, subject, kind, source, agent, text, data, created_at)
207
212
  VALUES
208
- ('test', 'hook.delivery', 'hooks', 'test', '', ${validData}, ${fiveMinAgo}),
209
- ('test', 'hook.delivery', 'hooks', 'test', '', ${missingDurData}, ${fiveMinAgo})
213
+ ('test', 'hook.delivery', 'system', 'hooks', 'test', '', ${validData}, ${fiveMinAgo}),
214
+ ('test', 'hook.delivery', 'system', 'hooks', 'test', '', ${missingDurData}, ${fiveMinAgo})
210
215
  `;
211
216
 
212
217
  const rows = (await sql`