refactor: scaffold module registries and default-module layout

Additive change — existing code paths still run via inline fallbacks.
Prepares core for per-module extractions in PR #3 onward.

Four registries added with empty defaults:
  - delivery action handlers (delivery.ts)
  - router inbound gate (router.ts)
  - response dispatcher (index.ts)
  - MCP tool self-registration (container/agent-runner/src/mcp-tools/server.ts)

Default modules moved to src/modules/ for signaling:
  - src/modules/typing/       (extracted from delivery.ts)
  - src/modules/mount-security/ (moved from src/mount-security.ts)

Both are imported directly by core — no hook, no registry. Removal
requires editing core imports.

Migrator now keys applied rows by name (uniqueness) so module
migrations can pick arbitrary version numbers. Stored version column
is auto-assigned as an applied-order sequence.

sqlite_master guards added around core calls into module-owned tables
(user_roles, agent_destinations, pending_questions). No-ops today;
load-bearing after the owning modules are extracted.

MODULE-HOOK markers placed at scheduling's two skill-edit sites
(host-sweep.ts recurrence call, poll-loop.ts pre-task gate). PR #4
replaces the marked blocks when scheduling moves to its module.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
This commit is contained in:
gavrielc
2026-04-18 14:46:19 +03:00
parent 1888ecc1e9
commit 4202041d0b
19 changed files with 480 additions and 234 deletions

View File

@@ -32,29 +32,34 @@ export function runMigrations(db: Database.Database): void {
name TEXT NOT NULL,
applied TEXT NOT NULL
);
CREATE UNIQUE INDEX IF NOT EXISTS idx_schema_version_name ON schema_version(name);
`);
const currentVersion =
(db.prepare('SELECT MAX(version) as v FROM schema_version').get() as { v: number | null })?.v ?? 0;
const pending = migrations.filter((m) => m.version > currentVersion);
// Uniqueness is keyed on `name`, not `version`. This lets module
// migrations (added later by install skills) pick arbitrary version
// numbers without coordinating across modules. `version` stays on
// the Migration object as an ordering hint within the barrel array;
// the stored `version` column is auto-assigned at insert time as an
// applied-order number.
const applied = new Set<string>(
(db.prepare('SELECT name FROM schema_version').all() as { name: string }[]).map((r) => r.name),
);
const pending = migrations.filter((m) => !applied.has(m.name));
if (pending.length === 0) return;
log.info('Running migrations', {
from: currentVersion,
to: pending[pending.length - 1].version,
count: pending.length,
});
log.info('Running migrations', { count: pending.length });
for (const m of pending) {
db.transaction(() => {
m.up(db);
const next =
(db.prepare('SELECT COALESCE(MAX(version), 0) + 1 AS v FROM schema_version').get() as { v: number }).v;
db.prepare('INSERT INTO schema_version (version, name, applied) VALUES (?, ?, ?)').run(
m.version,
next,
m.name,
new Date().toISOString(),
);
})();
log.info('Migration applied', { version: m.version, name: m.name });
log.info('Migration applied', { name: m.name });
}
}