@automagik/genie 4.260508.1 → 4.260508.2
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.260508.
|
|
3
|
+
"version": "4.260508.2",
|
|
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.260508.
|
|
3
|
+
"version": "4.260508.2",
|
|
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"
|
|
@@ -19,21 +19,43 @@
|
|
|
19
19
|
-- silent default-off that triggered the misleading message in the first
|
|
20
20
|
-- place.
|
|
21
21
|
--
|
|
22
|
+
-- Heal-not-wipe interaction with migration 061
|
|
23
|
+
-- --------------------------------------------
|
|
24
|
+
-- Migration 061 added `agents_id_shape_check` as `NOT VALID` so legacy
|
|
25
|
+
-- bare-name rows (archived by 050/053) stay in the table. Postgres still
|
|
26
|
+
-- enforces the constraint on every UPDATE — even when the id column is
|
|
27
|
+
-- not in the SET list — so an unconditional `UPDATE agents SET auto_resume
|
|
28
|
+
-- = true` errors out with `agents_id_shape_check` violation on any host
|
|
29
|
+
-- carrying archived bare-name rows. The whole boot path then fails,
|
|
30
|
+
-- because runMigrations runs inside getConnection's post-connect setup.
|
|
31
|
+
--
|
|
32
|
+
-- Filter the UPDATEs to UUID/dir-prefixed ids only. Bare-name rows are
|
|
33
|
+
-- already archived (state='archived', auto_resume=false locked by 050/053)
|
|
34
|
+
-- so flipping their auto_resume is meaningless — they are not resume
|
|
35
|
+
-- candidates regardless of the column value.
|
|
36
|
+
--
|
|
22
37
|
-- Idempotent: re-running this migration is a no-op on rows that are already
|
|
23
38
|
-- true and on a column whose default is already true.
|
|
24
39
|
|
|
25
40
|
BEGIN;
|
|
26
41
|
|
|
27
|
-
-- 1. Flip every existing false row
|
|
28
|
-
--
|
|
42
|
+
-- 1. Flip every existing false row that satisfies the id shape constraint.
|
|
43
|
+
-- Bare-name rows (archived legacy) are skipped — see header comment.
|
|
29
44
|
UPDATE agents
|
|
30
45
|
SET auto_resume = true
|
|
31
|
-
WHERE auto_resume = false
|
|
46
|
+
WHERE auto_resume = false
|
|
47
|
+
AND (id ~ '^[0-9a-f]{8}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{12}$'
|
|
48
|
+
OR id LIKE 'dir:%');
|
|
32
49
|
|
|
33
50
|
-- 2. Change the column DEFAULT so future spawns inherit the safe value.
|
|
34
51
|
-- NULLs (legacy rows that pre-date the column) become true — same as
|
|
35
|
-
-- the safe-default the runtime treats them as.
|
|
52
|
+
-- the safe-default the runtime treats them as. Same id-shape filter
|
|
53
|
+
-- applies for the same reason.
|
|
36
54
|
ALTER TABLE agents ALTER COLUMN auto_resume SET DEFAULT true;
|
|
37
|
-
UPDATE agents
|
|
55
|
+
UPDATE agents
|
|
56
|
+
SET auto_resume = true
|
|
57
|
+
WHERE auto_resume IS NULL
|
|
58
|
+
AND (id ~ '^[0-9a-f]{8}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{12}$'
|
|
59
|
+
OR id LIKE 'dir:%');
|
|
38
60
|
|
|
39
61
|
COMMIT;
|
|
@@ -0,0 +1,108 @@
|
|
|
1
|
+
import { afterAll, beforeAll, describe, expect, test } from 'bun:test';
|
|
2
|
+
import { randomUUID } from 'node:crypto';
|
|
3
|
+
import { readFile } from 'node:fs/promises';
|
|
4
|
+
import { join } from 'node:path';
|
|
5
|
+
import { type Sql, getConnection } from '../../lib/db.js';
|
|
6
|
+
import { DB_AVAILABLE, setupTestDatabase } from '../../lib/test-db.js';
|
|
7
|
+
|
|
8
|
+
const MIGRATION_PATH = join(import.meta.dir, '055_default_auto_resume_true.sql');
|
|
9
|
+
|
|
10
|
+
async function runMigration(sql: Sql): Promise<void> {
|
|
11
|
+
const sqlText = await readFile(MIGRATION_PATH, 'utf-8');
|
|
12
|
+
// Mirror runMigrations() in db-migrations.ts: wrap in sql.begin so the
|
|
13
|
+
// pool sees a single reserved connection. The migration file's own
|
|
14
|
+
// BEGIN/COMMIT becomes a no-op inside the outer transaction (postgres
|
|
15
|
+
// emits a "there is already a transaction in progress" warning and
|
|
16
|
+
// COMMIT closes the outer txn, same as production).
|
|
17
|
+
await sql.begin(async (tx) => {
|
|
18
|
+
await tx.unsafe(sqlText);
|
|
19
|
+
});
|
|
20
|
+
}
|
|
21
|
+
|
|
22
|
+
describe.skipIf(!DB_AVAILABLE)('migration 055 — default auto_resume true', () => {
|
|
23
|
+
let cleanup: () => Promise<void>;
|
|
24
|
+
|
|
25
|
+
beforeAll(async () => {
|
|
26
|
+
cleanup = await setupTestDatabase();
|
|
27
|
+
});
|
|
28
|
+
|
|
29
|
+
afterAll(async () => {
|
|
30
|
+
await cleanup();
|
|
31
|
+
});
|
|
32
|
+
|
|
33
|
+
test('does not fail when archived bare-name rows are present', async () => {
|
|
34
|
+
const sql = await getConnection();
|
|
35
|
+
|
|
36
|
+
// The template DB has migration 055 already applied. To replay it as if
|
|
37
|
+
// it were pending against a host that carries archived bare-name rows
|
|
38
|
+
// (legacy 050/053 grandfather), we have to re-create the failure shape:
|
|
39
|
+
// 1. Drop the id-shape CHECK so we can insert a bare-name row.
|
|
40
|
+
// 2. Insert one archived bare-name agent (mirrors production data).
|
|
41
|
+
// 3. Re-add the CHECK as NOT VALID (mirrors migration 061's pattern).
|
|
42
|
+
// 4. Reset a UUID row's auto_resume to false (something for 055 to flip).
|
|
43
|
+
// 5. Re-run 055.
|
|
44
|
+
// The pre-fix migration would error here with agents_id_shape_check;
|
|
45
|
+
// the post-fix migration filters bare-name rows out of the UPDATE.
|
|
46
|
+
await sql`ALTER TABLE agents DROP CONSTRAINT IF EXISTS agents_id_shape_check`;
|
|
47
|
+
|
|
48
|
+
const bareName = `legacy-bare-${Date.now()}`;
|
|
49
|
+
await sql`
|
|
50
|
+
INSERT INTO agents (id, role, custom_name, team, repo_path, started_at, state, auto_resume)
|
|
51
|
+
VALUES (${bareName}, 'legacy', NULL, 'legacy-team', '/tmp/legacy', now(), 'archived', false)
|
|
52
|
+
ON CONFLICT (id) DO NOTHING
|
|
53
|
+
`;
|
|
54
|
+
|
|
55
|
+
await sql.unsafe(
|
|
56
|
+
`ALTER TABLE agents
|
|
57
|
+
ADD CONSTRAINT agents_id_shape_check
|
|
58
|
+
CHECK (id ~ '^[0-9a-f]{8}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{12}$' OR id LIKE 'dir:%')
|
|
59
|
+
NOT VALID`,
|
|
60
|
+
);
|
|
61
|
+
|
|
62
|
+
const liveId = randomUUID();
|
|
63
|
+
await sql`
|
|
64
|
+
INSERT INTO agents (id, role, custom_name, team, repo_path, started_at, state, auto_resume)
|
|
65
|
+
VALUES (${liveId}, 'engineer', 'live-engineer', 'live-team', '/tmp/live', now(), 'idle', false)
|
|
66
|
+
ON CONFLICT (id) DO NOTHING
|
|
67
|
+
`;
|
|
68
|
+
|
|
69
|
+
// The pre-fix migration would throw here with
|
|
70
|
+
// `new row for relation "agents" violates check constraint "agents_id_shape_check"`.
|
|
71
|
+
await expect(runMigration(sql)).resolves.toBeUndefined();
|
|
72
|
+
|
|
73
|
+
const live = await sql<{ auto_resume: boolean }[]>`
|
|
74
|
+
SELECT auto_resume FROM agents WHERE id = ${liveId}
|
|
75
|
+
`;
|
|
76
|
+
expect(live[0].auto_resume).toBe(true);
|
|
77
|
+
|
|
78
|
+
const bare = await sql<{ auto_resume: boolean }[]>`
|
|
79
|
+
SELECT auto_resume FROM agents WHERE id = ${bareName}
|
|
80
|
+
`;
|
|
81
|
+
// Bare-name rows are archived legacy; their auto_resume is intentionally
|
|
82
|
+
// not flipped because the row would fail the id-shape check.
|
|
83
|
+
expect(bare[0].auto_resume).toBe(false);
|
|
84
|
+
});
|
|
85
|
+
|
|
86
|
+
test('flips auto_resume on UUID and dir: rows, leaves NULL rows true', async () => {
|
|
87
|
+
const sql = await getConnection();
|
|
88
|
+
|
|
89
|
+
const uuidId = randomUUID();
|
|
90
|
+
const dirId = `dir:test-dir-${Date.now()}`;
|
|
91
|
+
await sql`
|
|
92
|
+
INSERT INTO agents (id, role, custom_name, team, repo_path, started_at, state, auto_resume)
|
|
93
|
+
VALUES
|
|
94
|
+
(${uuidId}, 'engineer', 'a', 'team-a', '/tmp/a', now(), 'idle', false),
|
|
95
|
+
(${dirId}, 'engineer', 'b', 'team-b', '/tmp/b', now(), 'idle', false)
|
|
96
|
+
ON CONFLICT (id) DO NOTHING
|
|
97
|
+
`;
|
|
98
|
+
|
|
99
|
+
await runMigration(sql);
|
|
100
|
+
|
|
101
|
+
const rows = await sql<{ id: string; auto_resume: boolean }[]>`
|
|
102
|
+
SELECT id, auto_resume FROM agents WHERE id IN (${uuidId}, ${dirId})
|
|
103
|
+
`;
|
|
104
|
+
for (const r of rows) {
|
|
105
|
+
expect(r.auto_resume).toBe(true);
|
|
106
|
+
}
|
|
107
|
+
});
|
|
108
|
+
});
|