Skip to content

fix(spawn): generateWorkerId returns uuid for agents_id_shape_check - #1627

Merged
namastex888 merged 1 commit into
devfrom
wish/worker-id-uuid
May 3, 2026
Merged

namastex888 merged 1 commit into
devfrom
wish/worker-id-uuid

Conversation

@namastex888

Copy link
Copy Markdown
Contributor

Summary

After migration 061 (agents_id_invariant_and_fk_lockdown) shipped in v4.260503.7/.8, every new spawn (both genie work and genie spawn) hits:

PostgresError: new row for relation "agents" violates check constraint "agents_id_shape_check"

Constraint requires agents.id to be a UUID (8-4-4-4-12 hex) or dir:% prefix. But generateWorkerId still produces the legacy team-role[-8hex] shape — neither matches.

Root cause

src/term-commands/agents.ts:446:

const base = role ? `${team}-${role}` : team;
if (!existing.some((w) => w.id === base)) return base;
const suffix = crypto.randomUUID().slice(0, 8);
return `${base}-${suffix}`;

Both return paths produce non-UUID strings. Constraint rejects every new INSERT.

Fix (1-line semantic change)

async function generateWorkerId(_team: string, _role?: string): Promise<string> {
  return crypto.randomUUID();
}

agents.id becomes a pure UUID surrogate key. agents.role and agents.customName still carry human-readable identity for display/listing.

Companion to PR #1626

#1626 fixed dispatch role-collision (engineer-3 colliding across wishes). This PR fixes the deeper INSERT-time constraint violation that surfaces after #1626 unblocks the role check. Both are needed.

Validation

  • bun run typecheck clean
  • After merge: genie work <wish> and genie spawn <role> produce agents with UUID id that pass agents_id_shape_check

Test plan

  • genie work autopg-distribution-cutover from a fresh shell dispatches G3 without agents_id_shape_check error
  • genie spawn engineer --team genie produces agent row with UUID id
  • Existing agents with legacy ids continue working (NOT VALID grandfather clause from migration 061)
  • No regression in genie ls, genie agent show <name> — name resolution still works

Out of scope (followups)

  • Wider unification of the 3 worker-id paths (TUI nextRoleSuffix, dispatch effectiveRole, this generateWorkerId) — wish-territory
  • Migration to backfill legacy non-UUID ids — separate effort

🤖 Generated with Claude Code

After migration 061 (agents_id_invariant_and_fk_lockdown), agents.id
must be either a UUID (8-4-4-4-12 hex) or start with 'dir:'. But
generateWorkerId still produced legacy team-role[-8hex] shape, so
every new spawn failed:

  PostgresError: new row for relation "agents" violates check
  constraint "agents_id_shape_check"

Symptom: genie work / genie spawn blocked across the board after the
.7/.8 update — no fresh agent could be persisted.

Root cause (agents.ts:446):
  base = team-role; if free return base else append uuid.slice(0,8)
Both branches produce non-UUID strings.

Fix: return crypto.randomUUID() directly. agents.role still carries
the human-readable role; agents.customName / display still works.
The id field becomes a pure surrogate key, matching the constraint
contract.

This unblocks every wish-work dispatch and direct genie spawn that
was hitting agents_id_shape_check after migration 061 landed.

Wider unification (TUI nextRoleSuffix path, dispatch wish-work role
naming) is wish-territory; this is the surgical unblocker for the
INSERT-time constraint violation.

Validation: bun run typecheck clean.

Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
@coderabbitai

coderabbitai Bot commented May 3, 2026

Copy link
Copy Markdown

Important

Review skipped

Auto reviews are disabled on base/target branches other than the default branch.

Please check the settings in the CodeRabbit UI or the .coderabbit.yaml file in this repository. To trigger a single review, invoke the @coderabbitai review command.

⚙️ Run configuration

Configuration used: Path: .coderabbit.yaml

Review profile: ASSERTIVE

Plan: Pro

Run ID: ddfd9792-cd31-4805-bc91-f63b8c21ced4

You can disable this status message by setting the reviews.review_status to false in the CodeRabbit configuration file.

Use the checkbox below for a quick retry:

  • 🔍 Trigger review
✨ Finishing Touches
🧪 Generate unit tests (beta)
  • Create PR with unit tests
  • Commit unit tests in branch wish/worker-id-uuid

Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out.

❤️ Share

Comment @coderabbitai help to get the list of available commands and usage tips.

@namastex888
namastex888 merged commit 3e2abf8 into dev May 3, 2026
16 checks passed

@gemini-code-assist gemini-code-assist Bot left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Code Review

This pull request updates the generateWorkerId function to always return a random UUID, ensuring compliance with database constraints introduced in a recent migration. However, the reviewer pointed out that this change introduces a regression in idempotency and will likely cause unique constraint violations during agent registration. The feedback suggests retrieving the existing agent's UUID from the registry to maintain stability and avoid database errors.

Comment on lines +446 to 451
async function generateWorkerId(_team: string, _role?: string): Promise<string> {
// Always return a UUID. agents.id constraint (migration 061) requires
// UUID shape or 'dir:%' prefix. Legacy team-role-suffix shape failed
// agents_id_shape_check on every new spawn after that migration.
return crypto.randomUUID();
}

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

high

Returning a fresh crypto.randomUUID() here introduces a regression in idempotency and a high risk of database errors due to unique constraint violations.

  1. Unique Constraint Violation: In handleWorkerSpawn, registry.findOrCreateAgent is called at line 2621. This function ensures an agent identity exists for the given (custom_name, team) and returns its UUID id. If generateWorkerId then returns a different random UUID, the subsequent registry.register call (line 665, via ctx.workerId) will attempt to insert a new row with the same custom_name and team but a different primary key id. This will trigger a unique constraint violation on the (custom_name, team) composite key.
  2. Loss of Stability: The previous implementation produced a stable ID based on team and role. Using a random UUID means every spawn attempt for the same role will try to create a new agent record instead of updating the existing one.
  3. Incomplete Fix: This change only addresses the legacy path. The identity.workerId path in handleWorkerSpawn (line 2641) still uses resolveSpawnIdentity, which returns non-UUID strings (e.g., engineer or engineer-abcd). Standard spawns will likely still fail the agents_id_shape_check constraint.

To fix this properly while staying within the current architecture, this function should look up the existing agent's UUID from the registry.

async function generateWorkerId(team: string, role?: string): Promise<string> {
  // Always return a UUID. agents.id constraint (migration 061) requires
  // UUID shape or 'dir:%' prefix. We look up the existing agent identity
  // to ensure we use its canonical UUID and avoid unique constraint violations
  // on (custom_name, team) during registration.
  const name = role || team;
  const existing = await registry.getAgentByName(name, team);
  if (existing) return existing.id;

  return crypto.randomUUID();
}

namastex888 added a commit that referenced this pull request May 3, 2026
After migration 061 (agents.id became UUID-shaped) and PR #1627
(generateWorkerId returns UUID), resolveSpawnIdentity still ran a
SELECT id FROM agents WHERE id = ${name} with role-name strings,
hitting:

  PostgresError: invalid input syntax for type uuid: "engineer-4-eac7"

This blocked every fresh dispatch where the role name is not a UUID
(every wish-work dispatch, every direct genie spawn).

Fix: at function entry, if name is not UUID-shaped, skip the
canonical-lookup query and return a fresh canonical with a generated
UUID workerId immediately. Role-based dispatch continues working;
UUID-keyed lookups still flow through the original code path.

Validation: bun run typecheck clean.

Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
namastex888 added a commit that referenced this pull request May 4, 2026
After PR #1627 (generateWorkerId returns UUID), resolveSpawnIdentity
still ran SELECT id FROM agents WHERE id = ${name} with role-name
strings, hitting:

  PostgresError: invalid input syntax for type uuid: "engineer-4-eac7"

This blocks every fresh dispatch where the role name is not a UUID
(every wish-work, every direct genie spawn).

Fix: at function entry, if name is not UUID-shaped, skip the
canonical-lookup query and return a fresh canonical with a generated
UUID workerId. Role-based dispatch continues working; UUID-keyed
lookups still flow through the original code path.

Validation: bun run typecheck clean.

Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant