fix(migrate-v2): reset auto-created messaging_group policy on re-run

If 1b-db is re-run after the v2 service has already started (e.g.
recovering from an earlier failure), the messaging_group it would
otherwise create may already exist — auto-created by the runtime router
on the first inbound message, with the router's default
unknown_sender_policy ('request_approval'), not the migration's intent
('public'). The previous reuse path skipped creation but never updated
the policy, so re-runs left the bot hanging every message waiting for
an approver that wasn't seeded yet.

When reusing an existing row that has zero wired agent_groups (signal
of a router auto-create), reset the policy to 'public'. Once any wiring
exists, the user has had a chance to tighten via the skill — leave it.

Also adds a CHANGELOG entry covering this and the two sibling fixes
(Discord DM resolution, symlink skip in copyTree).
This commit is contained in:
Gavriel Cohen
2026-05-02 16:07:43 +00:00
committed by exe.dev user
parent 7dbedad9bd
commit 3b5e5a24f4
2 changed files with 18 additions and 1 deletions

View File

@@ -7,6 +7,7 @@ For detailed release notes, see the [full changelog on the documentation site](h
## [Unreleased] ## [Unreleased]
- **v1 → v2 migration.** Run `bash migrate-v2.sh` from the v2 checkout. Finds your v1 install (sibling directory or `NANOCLAW_V1_PATH`), merges `.env`, seeds the v2 DB from `registered_groups`, copies group folders (`CLAUDE.md``CLAUDE.local.md`), copies session data with conversation continuity, ports scheduled tasks, interactively selects and installs channels (clack multiselect), copies container skills, builds the agent container, and offers a service switchover to test. Hands off to Claude (`/migrate-from-v1`) for owner seeding, access policy, CLAUDE.md cleanup, and fork customization porting. See [docs/migration-dev.md](docs/migration-dev.md) and [docs/v1-to-v2-changes.md](docs/v1-to-v2-changes.md). - **v1 → v2 migration.** Run `bash migrate-v2.sh` from the v2 checkout. Finds your v1 install (sibling directory or `NANOCLAW_V1_PATH`), merges `.env`, seeds the v2 DB from `registered_groups`, copies group folders (`CLAUDE.md``CLAUDE.local.md`), copies session data with conversation continuity, ports scheduled tasks, interactively selects and installs channels (clack multiselect), copies container skills, builds the agent container, and offers a service switchover to test. Hands off to Claude (`/migrate-from-v1`) for owner seeding, access policy, CLAUDE.md cleanup, and fork customization porting. See [docs/migration-dev.md](docs/migration-dev.md) and [docs/v1-to-v2-changes.md](docs/v1-to-v2-changes.md).
- **Migration fixes.** `1b-db` now resolves Discord DMs as `discord:@me:<id>` (previously skipped any v1 chat that wasn't a guild channel — a blocker for personal-bot installs). `1c-groups` skips symlinks instead of following them (a single broken `.claude-shared.md → /app/CLAUDE.md` no longer aborts the whole copy). When `1b-db` reuses an auto-created `messaging_group` with no wired agents, its `unknown_sender_policy` is now reconciled to the migration's `public` default.
## [2.0.0] - 2026-04-22 ## [2.0.0] - 2026-04-22

View File

@@ -22,7 +22,9 @@ import {
createMessagingGroup, createMessagingGroup,
createMessagingGroupAgent, createMessagingGroupAgent,
getMessagingGroupAgentByPair, getMessagingGroupAgentByPair,
getMessagingGroupAgents,
getMessagingGroupByPlatform, getMessagingGroupByPlatform,
updateMessagingGroup,
} from '../../src/db/messaging-groups.js'; } from '../../src/db/messaging-groups.js';
import { runMigrations } from '../../src/db/migrations/index.js'; import { runMigrations } from '../../src/db/migrations/index.js';
import { readEnvFile } from '../../src/env.js'; import { readEnvFile } from '../../src/env.js';
@@ -147,7 +149,15 @@ async function main(): Promise<void> {
ag = getAgentGroupByFolder(g.folder)!; ag = getAgentGroupByFolder(g.folder)!;
} }
// messaging_group — one per (channel_type, platform_id) // messaging_group — one per (channel_type, platform_id).
//
// If the row already exists *and* has zero wired agent_groups, it
// was almost certainly auto-created by the runtime router on an
// inbound message (which uses 'request_approval' or similar — not
// the migration's 'public'). Reset its policy to match what the
// migration would have set if it had created the row first. Once
// any wiring exists, the user has had a chance to tighten the
// policy via the skill — leave it alone.
let mg = getMessagingGroupByPlatform(channelType, platformId); let mg = getMessagingGroupByPlatform(channelType, platformId);
if (!mg) { if (!mg) {
createMessagingGroup({ createMessagingGroup({
@@ -160,6 +170,12 @@ async function main(): Promise<void> {
created_at: createdAt, created_at: createdAt,
}); });
mg = getMessagingGroupByPlatform(channelType, platformId)!; mg = getMessagingGroupByPlatform(channelType, platformId)!;
} else if (
mg.unknown_sender_policy !== 'public' &&
getMessagingGroupAgents(mg.id).length === 0
) {
updateMessagingGroup(mg.id, { unknown_sender_policy: 'public' });
mg = getMessagingGroupByPlatform(channelType, platformId)!;
} }
// messaging_group_agents — wire them // messaging_group_agents — wire them