iranti-control-plane 0.4.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,65 @@
1
+ -- Migration: 001_create_staff_events
2
+ -- Spec ref: CP-T001 §3 (Option 1 schema)
3
+ -- Creates the staff_events table for the Iranti Control Plane event stream.
4
+ --
5
+ -- The `event_id` column name matches CP-T001 spec (not generic `id`).
6
+ -- CHECK constraints on staff_component and level enforce data integrity.
7
+ -- All INSERT statements from application code use parameterized queries only.
8
+
9
+ CREATE TABLE IF NOT EXISTS staff_events (
10
+ -- Primary key: UUID generated by DB
11
+ event_id UUID PRIMARY KEY DEFAULT gen_random_uuid(),
12
+
13
+ -- Timestamp: when the event occurred (NOT NULL — every event must have a timestamp)
14
+ timestamp TIMESTAMPTZ NOT NULL DEFAULT now(),
15
+
16
+ -- Staff component: which subsystem emitted this event
17
+ -- Constrained to the 4 known components for data integrity
18
+ staff_component VARCHAR(32) NOT NULL
19
+ CHECK (staff_component IN ('Librarian', 'Attendant', 'Archivist', 'Resolutionist')),
20
+
21
+ -- Action type: what happened within the component
22
+ action_type VARCHAR(64) NOT NULL,
23
+
24
+ -- The agent whose operation triggered this event
25
+ agent_id TEXT, -- Nullable: may be unknown for adapter-inferred events
26
+
27
+ -- The source surface (mcp, api, cli, etc.)
28
+ source TEXT,
29
+
30
+ -- Entity targeting fields (all nullable — not all events target an entity)
31
+ entity_type TEXT,
32
+ entity_id TEXT,
33
+ key TEXT,
34
+
35
+ -- Human-readable note from the triggering operation
36
+ reason TEXT,
37
+
38
+ -- Event level: audit (always on) or debug (configurable)
39
+ level VARCHAR(16) NOT NULL DEFAULT 'audit'
40
+ CHECK (level IN ('audit', 'debug')),
41
+
42
+ -- Component-specific supplementary data (schema varies by actionType)
43
+ metadata JSONB
44
+ );
45
+
46
+ -- Index 1: Primary query pattern — time-ordered event stream
47
+ -- Supports: SELECT * FROM staff_events ORDER BY timestamp DESC
48
+ -- Supports: SELECT * FROM staff_events WHERE timestamp > :cursor ORDER BY timestamp ASC
49
+ CREATE INDEX IF NOT EXISTS idx_staff_events_timestamp ON staff_events (timestamp DESC);
50
+
51
+ -- Index 2: Filter by Staff component
52
+ -- Supports: WHERE staff_component = 'Librarian'
53
+ CREATE INDEX IF NOT EXISTS idx_staff_events_component ON staff_events (staff_component);
54
+
55
+ -- Index 3: Filter by entity (compound — covers both type+id and type-only queries)
56
+ -- Supports: WHERE entity_type = 'ticket' AND entity_id = 'cp_t001'
57
+ -- Supports: WHERE entity_type = 'ticket'
58
+ CREATE INDEX IF NOT EXISTS idx_staff_events_entity ON staff_events (entity_type, entity_id);
59
+
60
+ -- Index 4: Filter by level (most queries filter to level='audit')
61
+ CREATE INDEX IF NOT EXISTS idx_staff_events_level ON staff_events (level);
62
+
63
+ -- Index 5: Filter by agent
64
+ -- Supports: WHERE agent_id = 'product_manager'
65
+ CREATE INDEX IF NOT EXISTS idx_staff_events_agent ON staff_events (agent_id);
@@ -0,0 +1,46 @@
1
+ -- Migration: 002_create_archive_flags
2
+ -- Spec ref: CP-T049 (Archivist Decision Transparency)
3
+ --
4
+ -- Flag storage decision (OQ-1):
5
+ -- A separate `archive_flags` table was chosen over a JSONB column on the archive
6
+ -- row for the following reasons:
7
+ --
8
+ -- 1. The archive table is an upstream Iranti schema — altering it with an ALTER TABLE
9
+ -- risks colliding with upstream migrations and makes the control plane's concerns
10
+ -- bleed into Iranti's core schema.
11
+ --
12
+ -- 2. A relational table gives us a proper FK with CASCADE DELETE, so flags are
13
+ -- automatically cleaned up if the archive row is ever garbage-collected by Iranti.
14
+ -- The ticket risk assessment specifically called this out as a mitigation concern.
15
+ --
16
+ -- 3. It is easier to query "all flagged facts" with a JOIN than to scan JSONB columns
17
+ -- across the entire archive table.
18
+ --
19
+ -- 4. Flags are a control-plane-owned concept; keeping them in a control-plane-owned
20
+ -- table keeps the schema responsibility clean.
21
+
22
+ CREATE TABLE IF NOT EXISTS archive_flags (
23
+ -- Primary key
24
+ id UUID PRIMARY KEY DEFAULT gen_random_uuid(),
25
+
26
+ -- The archive row this flag refers to. Stored as TEXT because the archive table's
27
+ -- id column type may vary (bigint, uuid, or other) — we store it as a string reference
28
+ -- and do NOT add a FK constraint so we don't depend on the archive table's PK type.
29
+ -- If a FK is desired after confirming the archive id type, add it with CASCADE DELETE.
30
+ archive_id TEXT NOT NULL,
31
+
32
+ -- Operator note — why they are flagging this fact
33
+ note TEXT,
34
+
35
+ -- When the flag was created
36
+ flagged_at TIMESTAMPTZ NOT NULL DEFAULT now(),
37
+
38
+ -- Which operator/agent created the flag (from session context; nullable)
39
+ created_by TEXT
40
+ );
41
+
42
+ -- Index: look up flags by archive_id (primary access pattern)
43
+ CREATE INDEX IF NOT EXISTS idx_archive_flags_archive_id ON archive_flags (archive_id);
44
+
45
+ -- Only one active flag per archive row (operators can edit the note by clearing and re-flagging)
46
+ CREATE UNIQUE INDEX IF NOT EXISTS idx_archive_flags_unique_per_row ON archive_flags (archive_id);
@@ -0,0 +1,16 @@
1
+ -- Migration: 003_staff_events_metrics_index
2
+ -- Spec ref: CP-T060 (Metrics Dashboard — Backend)
3
+ --
4
+ -- Adds a compound index on staff_events (timestamp, agent_id, action_type) to
5
+ -- support the aggregate GROUP BY queries used by the metrics endpoints.
6
+ --
7
+ -- The existing idx_staff_events_timestamp covers time-ordered streaming (timestamp DESC).
8
+ -- This compound index covers the metrics query pattern:
9
+ -- WHERE timestamp > $period_start AND action_type = ANY($types)
10
+ -- GROUP BY DATE(timestamp), agent_id
11
+ --
12
+ -- Using BRIN would not help here because we need per-agent selectivity.
13
+ -- A B-tree compound index is appropriate for this query shape.
14
+
15
+ CREATE INDEX IF NOT EXISTS idx_staff_events_metrics
16
+ ON staff_events (timestamp, agent_id, action_type);
package/package.json ADDED
@@ -0,0 +1,59 @@
1
+ {
2
+ "name": "iranti-control-plane",
3
+ "version": "0.4.0",
4
+ "description": "Operator control plane for Iranti - local-first agent memory management",
5
+ "bin": {
6
+ "iranti-cp": "bin/iranti-cp.js"
7
+ },
8
+ "files": [
9
+ "bin/",
10
+ "dist/server/bundle.cjs",
11
+ "public/control-plane/",
12
+ "migrations/"
13
+ ],
14
+ "engines": {
15
+ "node": ">=18.0.0"
16
+ },
17
+ "scripts": {
18
+ "dev:server": "node scripts/dev-server.mjs",
19
+ "dev:client": "cd src/client && npm run dev",
20
+ "dev": "node scripts/dev.mjs",
21
+ "dev:reset": "node scripts/dev-reset.mjs",
22
+ "build:client": "cd src/client && npm run build",
23
+ "build:server": "cd src/server && npm run build",
24
+ "build": "npm run build:client && npm run build:server && npm run package:bundle",
25
+ "prepack": "npm run build",
26
+ "start": "cd src/server && npm start",
27
+ "migrate": ".\\src\\server\\node_modules\\.bin\\tsx src/server/migrations/runner.ts",
28
+ "setup": "node scripts/setup-wizard.js",
29
+ "dev-setup": "npm install --prefix src/server && npm install --prefix src/client",
30
+ "package:bundle": "node scripts/package/bundle.mjs",
31
+ "package:windows": "node scripts/package/archive/build-windows.mjs",
32
+ "package:macos": "node scripts/package/archive/build-macos.mjs",
33
+ "package:linux": "node scripts/package/archive/build-linux.mjs",
34
+ "package:all": "npm run package:bundle && npm run package:windows && npm run package:macos && npm run package:linux"
35
+ },
36
+ "devDependencies": {
37
+ "@clack/prompts": "^1.1.0",
38
+ "concurrently": "^8.2.2"
39
+ },
40
+ "repository": {
41
+ "type": "git",
42
+ "url": "git+https://github.com/nfemmanuel/iranti-control-plane.git"
43
+ },
44
+ "homepage": "https://github.com/nfemmanuel/iranti-control-plane#readme",
45
+ "bugs": {
46
+ "url": "https://github.com/nfemmanuel/iranti-control-plane/issues"
47
+ },
48
+ "keywords": [
49
+ "iranti",
50
+ "control-plane",
51
+ "agent-memory",
52
+ "operator-dashboard",
53
+ "mcp",
54
+ "claude-code",
55
+ "codex"
56
+ ],
57
+ "author": "NF",
58
+ "license": "AGPL-3.0-or-later"
59
+ }