Skip to content

chore: promote dev → main (omni serve Model A) - #2516

Merged
namastex888 merged 24 commits into
mainfrom
dev
Jul 4, 2026
Merged

namastex888 merged 24 commits into
mainfrom
dev

Conversation

@namastex888

@namastex888 namastex888 commented Jul 4, 2026 •

Copy link
Copy Markdown
Contributor

Promotes the omni-serve Model A work (persona + session/resume + streaming + ⏳→✅ reaction ack) from dev to main. Landed on dev via #2515 (replanted from the closed #2514 so PRs to main come from dev). Verified live end-to-end against the k8s omni deploy. NOTE: dev is behind main by 29 commits (pre-existing divergence) — this is a merge (not ff), so it only ADDS the Model A commits to main; a dev↔main resync is a separate hygiene follow-up.

Summary by CodeRabbit

  • New Features

    • Added a Hermes Genie “native surface” with read-only tools, slash commands, CLI support, advisory hooks, schemas, and skills.
    • Added route-level persona support so runs can optionally append a route-specific persona instruction file.
  • Bug Fixes

    • Improved agent session handling with resume-first behavior and deterministic session IDs.
    • Upgraded streaming output parsing and tightened status/reaction correlation; reaction acknowledgments now use the dedicated reaction endpoint format.
  • Documentation

    • Added Genie Hermes plugin docs, mutation-gates/native-surface references, a new draft WISH, and a Hermes Genie profile README.

namastex888 and others added 6 commits July 4, 2026 16:47
…odel A)

Enhance the `genie omni serve` inbound one-shot into "Model A":

- defaultSpawnClaude now runs `claude -p --output-format stream-json --verbose
  --session-id <uuid> [--append-system-prompt-file <persona>]`, parsing the
  final reply out of the NDJSON stream (terminal result event → assistant text
  deltas → raw stdout fallback). Arg construction + parsing are extracted into
  pure, exported buildClaudeArgs / extractStreamJsonReply.
- SpawnClaudeOpts gains optional personaFile + sessionId; the runner resolves
  personaFile = route.persona ?? <repo>/AGENTS.md and derives a STABLE
  deterministicSessionId(instance, chat) so a conversation resumes across
  messages.
- Route the inbound WhatsApp stanza id (messageId) through handleMessage →
  startRoutedRun → runOneShot and set a ⏳→✅/❌ status reaction on it,
  route-scoped to (route.instance, route.chat). Generalizes the existing
  approval-scoped emitStatusReaction into a shared emitReaction seam; the route
  ack records no glyph and skips the reconciliation guard, stays fire-and-forget
  (drained by whenIdle), and never throws.
- Extend the OmniRoute config (schema + runtime type) with optional persona.

The approval flow is untouched (same ⏳/✅/❌ behaviour, glyph recording and
reconciliation). Adds 12 tests covering argv, stream-json parsing, session-id
stability, route-scoped ⏳→✅/❌ acks, the no-messageId no-op, and persona
resolution — all with injected spawnClaude + setReaction, zero fork/HTTP.
…odel A review)

Address the BLOCKED review of the Model A one-shot:

- CRITICAL — `claude --session-id <id>` CREATES a session and exits 1 "already
  in use" on every turn after the first, so multi-turn was broken. Add a
  resume-first orchestration (`runClaudeSession`): attempt `--resume <id>` first
  (one spawn for turn 2..N and across `omni serve` restarts, since the session
  persists on disk) and fall back to `--session-id <id>` only when the session
  is missing. Verified LIVE against claude 2.1.201 — the missing-session error is
  actually "No conversation found with session ID" (NOT the "not found" the
  review assumed), so the detection regex was broadened accordingly. Extracted a
  testable `RawClaudeSpawn` seam so the resume/create branching is unit-tested
  without a fork; `buildClaudeArgs` gains a `create | resume` mode.
- MEDIUM — `extractStreamJsonReply` ignored `is_error`: an error/empty terminal
  result returned the raw NDJSON blob, published as a ✅ reply. It now returns
  `{ reply, isError }`; a soft-error (is_error / non-success subtype / empty
  result) sets `isError`, and `runOneShot` treats `(exitCode !== 0 || isError)`
  as failure → error notice + ❌, never the raw blob. `SpawnClaudeResult` gains
  `isError?`.
- LOW — `resolvePersonaFile` now existsSync-checks an explicit `route.persona`
  too (a typo'd path would make claude exit 1 every run); a missing path is
  logged and dropped.

Also close stdin (`ignore`) in the default fork so claude doesn't wait ~3s for
piped input. `bun run check` green (+9 tests: resume-first create/resume/no-
fallback/already-in-use regression, error+empty result parsing, soft-error ack,
missing-persona drop). Live end-to-end: turn 1 creates ("OK"), turn 2 resumes
and recalls context ("Quokka").
The bare `|not found` alternative matched ANY "not found" stderr (e.g. a
"model not found" / MCP "… not found") on a turn whose session actually
exists, triggering a spurious `--session-id` create that then errors
"already in use" (one wasted spawn + a worse error). Each remaining
alternative is session-scoped: `no conversation found` (claude 2.1.201),
`no such session` / `session not found` (legacy phrasings). Add a test
asserting a generic "… not found" resume failure is NOT treated as a
missing session (resume-only, no create fallback).
…names

The route ⏳→✅ ack (and approval acks) POSTed reactions to /api/v2/messages

with {chatId, reaction}, but omni's send endpoint rejects that (400). Omni's

dedicated reaction route is POST /api/v2/messages/send/reaction expecting

{instanceId, to, messageId, emoji}. Corrected the endpoint + field names.
feat(omni): Model A — persona + session + streaming + ⏳→✅ ack for genie omni serve
@coderabbitai

coderabbitai Bot commented Jul 4, 2026 •

Copy link
Copy Markdown

Review Change Stack

Warning

Review limit reached

@namastex888, you've reached your PR review limit, so we couldn't start this review.

Next review available in: 7 minutes

Enable usage-based reviews in Billing to review now. Otherwise, wait until the next included review is available.
You're only billed for reviews past your plan's rate limits ($0.25/file).

How can I continue?

After more reviews become available, a review can be triggered using the @coderabbitai review command as a PR comment. Alternatively, push new commits to this PR.

To avoid repeated limits, reduce automatic review volume by pausing incremental auto-reviews earlier, using label-based review opt-in, excluding WIP or generated PR titles, or requesting reviews manually when the PR is ready. If your team needs uninterrupted high-volume reviews, an organization admin can enable usage-based reviews.

How do review limits work?

CodeRabbit enforces per-developer PR review limits for each organization. Most developers receive the normal plan review availability.

For paid Pro and Pro+ PR reviews, CodeRabbit uses adaptive limits for sustained high-volume activity. When a developer's recent PR review activity reaches the 95th percentile or higher among CodeRabbit users, additional reviews become available more gradually as earlier reviews age out of the rolling window.

Please refer docs for additional details.

Review details
⚙️ Run configuration

Configuration used: Path: .coderabbit.yaml

Review profile: ASSERTIVE

Plan: Pro

Run ID: ddb64bae-6258-4cbc-a7ff-f092d914b62e

📥 Commits

Reviewing files that changed from the base of the PR and between ab4c8a8 and 1308e4c.

📒 Files selected for processing (41)
  • .genie/wishes/skills-fable5-revamp/WISH.md
  • .genie/wishes/skills-fable5-revamp/conventions.md
  • .genie/wishes/skills-fable5-revamp/reports/g8-v4-footprint.md
  • .genie/wishes/skills-fable5-revamp/verification.md
  • install.sh
  • scripts/skills-lint.ts
  • skills/brainstorm/SKILL.md
  • skills/brainstorm/references/design-template.md
  • skills/council/SKILL.md
  • skills/council/members/config.md
  • skills/council/templates/report.md
  • skills/docs/SKILL.md
  • skills/dream/SKILL.md
  • skills/fix/SKILL.md
  • skills/genie-hacks/SKILL.md
  • skills/genie-hacks/references/catalog.md
  • skills/genie-hacks/references/contributing.md
  • skills/genie/SKILL.md
  • skills/genie/reference/lifecycle.md
  • skills/learn/SKILL.md
  • skills/omni/SKILL.md
  • skills/pm/SKILL.md
  • skills/pm/references/modes.md
  • skills/refine/SKILL.md
  • skills/refine/prompts/optimizer.md
  • skills/report/SKILL.md
  • skills/report/references/issue-template.md
  • skills/review/SKILL.md
  • skills/trace/SKILL.md
  • skills/wish/SKILL.md
  • skills/wizard/SKILL.md
  • skills/work/SKILL.md
  • src/genie-commands/__tests__/update.test.ts
  • src/genie-commands/install.test.ts
  • src/genie-commands/install.ts
  • src/genie-commands/legacy-v4.test.ts
  • src/genie-commands/legacy-v4.ts
  • src/genie-commands/uninstall.ts
  • src/genie-commands/update.ts
  • src/genie.ts
  • src/lib/interactivity.ts
📝 Walkthrough

Walkthrough

This PR upgrades Omni's one-shot Claude execution with persona injection, deterministic resumable sessions, stream-json reply parsing, and route-scoped reaction acks; adds a new Hermes Genie native plugin surface with bridging, commands, hooks, schemas, docs, skills, tests; and bumps release versions across manifests.

Changes

Omni Model A

Layer / File(s) Summary
Persona config schema
src/lib/omni-config.ts, src/types/genie-config.ts
OmniRoute gains an optional persona path field with documented AGENTS.md fallback behavior.
Stream-json and resumable sessions
src/lib/omni-runner.ts
Adds buildClaudeArgs, extractStreamJsonReply, deterministicSessionId, and runClaudeSession for resume-first Claude execution with NDJSON reply extraction.
Reaction endpoint and ack wiring
src/lib/omni-runner.ts
Reaction HTTP call moves to /api/v2/messages/send/reaction; emitReaction is generalized into emitStatusReaction/emitRouteReaction; inbound messageId threads through handleMessage→startRoutedRun→runOneShot for ⏳→✅/❌ acks; persona resolution checks filesystem existence.
Tests
src/lib/omni-runner.test.ts
New tests cover argv building, stream-json extraction, session resume semantics, route ack ordering/failure handling, and persona/session threading.

Estimated code review effort: 4 (Complex) | ~60 minutes

Hermes Genie native surface

Layer / File(s) Summary
Bridge, schemas, tools, commands, hooks, registration
plugins/hermes-genie/genie_bridge.py, plugins/hermes-genie/schemas.py, plugins/hermes-genie/__init__.py, plugins/hermes-genie/commands.py, plugins/hermes-genie/hooks.py, plugins/hermes-genie/plugin.yaml
Adds argv-only safe subprocess bridge, ref validation, structured payload envelope, seven read-only Genie tool handlers, slash/CLI dispatch, advisory hooks, and plugin manifest/registration.
Docs, install/smoke scripts, skills, wish, and contract tests
plugins/hermes-genie/README.md, plugins/hermes-genie/references/*, plugins/hermes-genie/scripts/*, plugins/hermes-genie/skills/*, plugins/hermes-genie/tests/*, .genie/wishes/hermes-khaw-native-surface/WISH.md, plugins/genie/README.md, profiles/hermes/genie/README.md
Documents the read-only MVP boundary and mutation gates, provides install/smoke scripts, defines cockpit/review/work/bridge skills, adds the cross-repo wish plan, and adds contract/unit tests for manifest, tools, bridge, and commands.

Estimated code review effort: 4 (Complex) | ~75 minutes

Version sync

Layer / File(s) Summary
Release version bump
.claude-plugin/marketplace.json, package.json, plugins/genie/.claude-plugin/plugin.json, plugins/genie/package.json
Version bumped from 5.260703.5 to 5.260704.3 across manifests.

Estimated code review effort: 1 (Trivial) | ~2 minutes

Sequence Diagram(s)

sequenceDiagram
  participant WhatsApp as handleMessage
  participant Route as startRoutedRun
  participant OneShot as runOneShot
  participant Session as runClaudeSession
  participant Claude as claude CLI
  participant API as messages/send/reaction

  WhatsApp->>Route: msg.messageId
  Route->>OneShot: messageId
  OneShot->>API: emit ⏳ before spawn
  OneShot->>Session: sessionId, personaFile
  Session->>Claude: --resume sessionId
  alt missing session
    Session->>Claude: retry with --session-id
  end
  Claude-->>Session: stream-json NDJSON
  Session-->>OneShot: stdout, exitCode, isError
  alt success
    OneShot->>API: emit ✅
  else failure or timeout
    OneShot->>API: emit ❌
  end
Loading
sequenceDiagram
  participant Hermes as commands.py / CLI
  participant Bridge as genie_bridge.py
  participant Genie as genie CLI

  Hermes->>Bridge: tool args and cwd
  Bridge->>Genie: argv list via subprocess.run
  Genie-->>Bridge: stdout/stderr
  Bridge-->>Hermes: structured JSON envelope
  Hermes->>Hermes: render outcome-first output
Loading

Possibly related PRs

  • automagik-dev/genie#2510: Modifies the same src/lib/omni-runner.ts reaction/ack lifecycle (status swapping, reaction endpoint/body shape, reconciliation guarding) that this PR extends into route-scoped acks.
🚥 Pre-merge checks | ✅ 4 | ❌ 1

❌ Failed checks (1 warning)

Check name Status Explanation Resolution
Out of Scope Changes check ⚠️ Warning Large Hermes plugin/docs/version-bump changes are unrelated to the linked omni serve issue and appear outside its scope. Split the Hermes plugin and documentation work into a separate PR or link the relevant issue that authorizes that scope.
✅ Passed checks (4 passed)
Check name Status Explanation
Description Check ✅ Passed Check skipped - CodeRabbit’s high-level summary is enabled.
Title check ✅ Passed The title is concise and accurately reflects the branch promotion and the main omni serve Model A change.
Linked Issues check ✅ Passed The changed Omni runner and config match the persona, deterministic session, streaming reply, and route-ack requirements.
Docstring Coverage ✅ Passed No functions found in the changed files to evaluate docstring coverage. Skipping docstring coverage check.
✨ Finishing Touches
🧪 Generate unit tests (beta)
  • Create PR with unit tests
  • Commit unit tests in branch dev

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.

@chatgpt-codex-connector chatgpt-codex-connector Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

💡 Codex Review

Here are some automated review suggestions for this pull request.

Reviewed commit: 63015670d9

ℹ️ About Codex in GitHub

Your team has set up Codex to review pull requests in this repo. Reviews are triggered when you

  • Open a pull request for review
  • Mark a draft as ready
  • Comment "@codex review".

If Codex has suggestions, it will comment; otherwise it will react with 👍.

Codex can also answer questions or update the PR. Try commenting "@codex address that feedback".

Comment thread src/lib/omni-runner.ts
);
publish(replySubject, buildRoutedReplyPayload(route.instance, route.chat, content, genId(), now()));
// ✅ once a genuine reply is published; ❌ on a non-zero exit or soft error.
emitRouteReaction(route, messageId, ok ? STATUS_APPROVED : STATUS_DENIED);

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

P2 Badge Serialize route status reactions

When the Claude turn is fast and the initial ⏳ HTTP call is still pending, this second emitRouteReaction starts another fire-and-forget HTTP request without waiting for the first. Because route acks use no guard or ordering, the API can process ✅/❌ before ⏳ and then have the slower ⏳ complete last, leaving a successful routed run marked pending on the inbound WhatsApp message. Please serialize route acks for a given message or wait for the pending reaction before sending the terminal one.

Useful? React with 👍 / 👎.

Comment thread src/lib/omni-runner.ts
// Thread the inbound WhatsApp stanza id so the run can ⏳→✅/❌ react on it.
const route = findRoute(instance, chat);
if (route) startRoutedRun(route, inboundId, body);
if (route) startRoutedRun(route, inboundId, body, msg.messageId);

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

P2 Badge Skip route acks for reaction frames

This passes msg.messageId to the route ack for every routed inbound before the later parseReaction check. For reaction frames, this field is the reacted-to message id rather than a new inbound text stanza, so a user reaction in a routed chat makes the run status mutate the message being reacted to (and can interfere with approval status glyphs if that chat is also routed). Gate routed runs/acks to non-reaction messages or avoid using msg.messageId for reaction payloads.

Useful? React with 👍 / 👎.

@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 introduces route-scoped run acknowledgments (using hourglass, check, and cross emojis), stable session continuity (via resume-first session threading), and support for custom route personas in the Omni runner. Key changes include parsing Claude's stream-json output, deriving deterministic session IDs, and updating the reaction API endpoint. The review feedback suggests several robustness improvements: adding a -- argument separator to prevent messages starting with hyphens from being parsed as CLI options, adding an early abort signal check in runClaudeSession to avoid spawning processes unnecessarily, and enhancing error notices by appending stdout context on non-zero exit codes.

Important

The consumer version of Gemini Code Assist on GitHub is being sunset. Starting June 18, 2026, new organization installations will be blocked, and all code review activity will officially cease on July 17, 2026.
For more details on the timeline and next steps, please review the Help Documentation.

Comment thread src/lib/omni-runner.ts
Comment on lines +147 to +155
return [
'-p',
'--output-format',
'stream-json',
'--verbose',
...sessionFlag,
...(opts.personaFile ? ['--append-system-prompt-file', opts.personaFile] : []),
opts.message,
];

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.

medium

If opts.message starts with a hyphen (e.g., --help or -v), it can be misinterpreted by the claude CLI as an option rather than the positional prompt argument. Inserting the standard -- argument separator before opts.message ensures that any user message starting with a hyphen is correctly treated as the prompt.

  return [
    '-p',
    '--output-format',
    'stream-json',
    '--verbose',
    ...sessionFlag,
    ...(opts.personaFile ? ['--append-system-prompt-file', opts.personaFile] : []),
    '--',
    opts.message,
  ];

Comment thread src/lib/omni-runner.ts
Comment on lines +286 to +296
export async function runClaudeSession(opts: SpawnClaudeOpts, rawSpawn: RawClaudeSpawn): Promise<SpawnClaudeResult> {
const base = { message: opts.message, sessionId: opts.sessionId ?? randomUUID(), personaFile: opts.personaFile };
const spawnOpts = { cwd: opts.cwd, signal: opts.signal };
const resumed = await rawSpawn(buildClaudeArgs({ ...base, mode: 'resume' }), spawnOpts);
if (resumed.exitCode === 0 || opts.signal.aborted || !NO_SESSION_RE.test(resumed.stderr)) {
return toSpawnResult(resumed);
}
// Session does not exist yet → create it (this spawn processes the message).
const created = await rawSpawn(buildClaudeArgs({ ...base, mode: 'create' }), spawnOpts);
return toSpawnResult(created);
}

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.

medium

If the abort signal is already aborted when runClaudeSession is called, we should avoid spawning any child processes. Adding an early guard check for opts.signal.aborted prevents unnecessary process spawning and potential unhandled errors.

Suggested change
export async function runClaudeSession(opts: SpawnClaudeOpts, rawSpawn: RawClaudeSpawn): Promise<SpawnClaudeResult> {
const base = { message: opts.message, sessionId: opts.sessionId ?? randomUUID(), personaFile: opts.personaFile };
const spawnOpts = { cwd: opts.cwd, signal: opts.signal };
const resumed = await rawSpawn(buildClaudeArgs({ ...base, mode: 'resume' }), spawnOpts);
if (resumed.exitCode === 0 || opts.signal.aborted || !NO_SESSION_RE.test(resumed.stderr)) {
return toSpawnResult(resumed);
}
// Session does not exist yet → create it (this spawn processes the message).
const created = await rawSpawn(buildClaudeArgs({ ...base, mode: 'create' }), spawnOpts);
return toSpawnResult(created);
}
export async function runClaudeSession(opts: SpawnClaudeOpts, rawSpawn: RawClaudeSpawn): Promise<SpawnClaudeResult> {
if (opts.signal.aborted) {
return { stdout: '', exitCode: -1, isError: true };
}
const base = { message: opts.message, sessionId: opts.sessionId ?? randomUUID(), personaFile: opts.personaFile };
const spawnOpts = { cwd: opts.cwd, signal: opts.signal };
const resumed = await rawSpawn(buildClaudeArgs({ ...base, mode: 'resume' }), spawnOpts);
if (resumed.exitCode === 0 || opts.signal.aborted || !NO_SESSION_RE.test(resumed.stderr)) {
return toSpawnResult(resumed);
}
// Session does not exist yet → create it (this spawn processes the message).
const created = await rawSpawn(buildClaudeArgs({ ...base, mode: 'create' }), spawnOpts);
return toSpawnResult(created);
}

Comment thread src/lib/omni-runner.ts
Comment on lines +727 to +731
const content = ok
? truncateReply(result.stdout, maxReplyChars)
: errorNotice(
result.exitCode !== 0 ? `exit code ${result.exitCode}` : result.stdout || 'agent returned an error',
);

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.

medium

When the process exits with a non-zero code, the error notice currently only displays the exit code, completely ignoring result.stdout. Since result.stdout contains the parsed output/error message from Claude Code's stream-json, appending it to the error notice when available provides much more helpful context for debugging.

Suggested change
const content = ok
? truncateReply(result.stdout, maxReplyChars)
: errorNotice(
result.exitCode !== 0 ? `exit code ${result.exitCode}` : result.stdout || 'agent returned an error',
);
const content = ok
? truncateReply(result.stdout, maxReplyChars)
: errorNotice(
result.exitCode !== 0
? 'exit code ' + result.exitCode + (result.stdout ? ': ' + result.stdout : '')
: result.stdout || 'agent returned an error',
);

@coderabbitai coderabbitai Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

Actionable comments posted: 1

🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

Inline comments:
In `@src/lib/omni-runner.test.ts`:
- Around line 971-1041: The tmpdir fixture cleanup in these `threadingRunner`
tests is duplicated with per-test try/finally blocks instead of using the shared
`afterEach` cleanup pattern. Update the three tests around `threadingRunner`,
`mkdtempSync`, and `rmSync` to use the existing `makeTmpDir(...)` helper (or
equivalent shared fixture setup) and centralize directory cleanup in
`afterEach`, removing the repeated per-test teardown logic.
🪄 Autofix (Beta)

Fix all unresolved CodeRabbit comments on this PR:

  • Push a commit to this branch (recommended)
  • Create a new PR with the fixes

ℹ️ Review info
⚙️ Run configuration

Configuration used: Path: .coderabbit.yaml

Review profile: ASSERTIVE

Plan: Pro

Run ID: 5031f05b-bd7f-4088-933a-139f01da5bd7

📥 Commits

Reviewing files that changed from the base of the PR and between 89f04c3 and 6301567.

📒 Files selected for processing (8)
  • .claude-plugin/marketplace.json
  • package.json
  • plugins/genie/.claude-plugin/plugin.json
  • plugins/genie/package.json
  • src/lib/omni-config.ts
  • src/lib/omni-runner.test.ts
  • src/lib/omni-runner.ts
  • src/types/genie-config.ts

Comment on lines +971 to +1041
test('threads a stable session id (same across messages) and the explicit route persona', async () => {
const dir = mkdtempSync(join(tmpdir(), 'genie-omni-explicit-'));
try {
const persona = join(dir, 'persona-A.md');
writeFileSync(persona, '# persona A');
const db = freshDb();
const seen: SeenSpawn[] = [];
const runner = threadingRunner(db, seen, [{ instance: INSTANCE, chat: ROUTE_CHAT, repo: ROUTE_REPO, persona }]);

runner.handleMessage(...mappedInboundWithId('m1', 'id1'));
await runner.whenIdle();
runner.handleMessage(...mappedInboundWithId('m2', 'id2'));
await runner.whenIdle();

expect(seen.length).toBe(2);
expect(seen[0].personaFile).toBe(persona);
expect(seen[0].sessionId).toBe(deterministicSessionId(INSTANCE, ROUTE_CHAT));
// Stable session id ⇒ the conversation resumes across messages.
expect(seen[1].sessionId).toBe(seen[0].sessionId);
} finally {
rmSync(dir, { recursive: true, force: true });
}
});

test('drops a typo’d explicit route.persona that does not exist (never passes it to claude)', async () => {
const db = freshDb();
const seen: SeenSpawn[] = [];
const missing = join(tmpdir(), 'genie-omni-does-not-exist', 'persona.md');
const runner = threadingRunner(db, seen, [
{ instance: INSTANCE, chat: ROUTE_CHAT, repo: ROUTE_REPO, persona: missing },
]);

runner.handleMessage(...mappedInboundWithId('m', 'id'));
await runner.whenIdle();

expect(seen[0].personaFile).toBeUndefined();
});

test('falls back to <repo>/AGENTS.md when route.persona is unset', async () => {
const dir = mkdtempSync(join(tmpdir(), 'genie-omni-persona-'));
try {
writeFileSync(join(dir, 'AGENTS.md'), '# persona');
const db = freshDb();
const seen: SeenSpawn[] = [];
const runner = threadingRunner(db, seen, [{ instance: INSTANCE, chat: ROUTE_CHAT, repo: dir }]);

runner.handleMessage(...mappedInboundWithId('m', 'id'));
await runner.whenIdle();

expect(seen[0].personaFile).toBe(join(dir, 'AGENTS.md'));
} finally {
rmSync(dir, { recursive: true, force: true });
}
});

test('resolves no persona when neither route.persona nor <repo>/AGENTS.md exists', async () => {
const dir = mkdtempSync(join(tmpdir(), 'genie-omni-nopersona-'));
try {
const db = freshDb();
const seen: SeenSpawn[] = [];
const runner = threadingRunner(db, seen, [{ instance: INSTANCE, chat: ROUTE_CHAT, repo: dir }]);

runner.handleMessage(...mappedInboundWithId('m', 'id'));
await runner.whenIdle();

expect(seen[0].personaFile).toBeUndefined();
} finally {
rmSync(dir, { recursive: true, force: true });
}
});
});

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

📐 Maintainability & Code Quality | 🔵 Trivial | ⚡ Quick win

Use afterEach for tmpdir cleanup instead of per-test try/finally.

Three tests each manually mkdtempSync/rmSync with a try/finally block. This duplicates boilerplate and diverges from the stated pattern of centralizing fixture cleanup in afterEach.

♻️ Suggested consolidation
+let tmpDirs: string[] = [];
+afterEach(() => {
+  for (const dir of tmpDirs) rmSync(dir, { recursive: true, force: true });
+  tmpDirs = [];
+});
+function makeTmpDir(prefix: string): string {
+  const dir = mkdtempSync(join(tmpdir(), prefix));
+  tmpDirs.push(dir);
+  return dir;
+}

Then each test drops its own try { ... } finally { rmSync(...) } wrapper in favor of makeTmpDir(...).

As per path instructions, **/*.test.{ts,tsx,js,jsx} files should "Use tmpdir with cleanup in afterEach for test fixtures."

🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@src/lib/omni-runner.test.ts` around lines 971 - 1041, The tmpdir fixture
cleanup in these `threadingRunner` tests is duplicated with per-test try/finally
blocks instead of using the shared `afterEach` cleanup pattern. Update the three
tests around `threadingRunner`, `mkdtempSync`, and `rmSync` to use the existing
`makeTmpDir(...)` helper (or equivalent shared fixture setup) and centralize
directory cleanup in `afterEach`, removing the repeated per-test teardown logic.

Source: Path instructions

namastex888 and others added 11 commits July 4, 2026 18:12
… seed from main

The profile seed (profiles/hermes/genie/, commit b112309) exists only on
main lineage; ported verbatim so Group 3 can modify the README on this
dev-cut branch. The dev->main merge will reconcile on identical blobs
except the README, which carries the wish's additive edit.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01BEJCsZTjxLyrM8BQKjGEVs
…only tools

Hermes-native surface for Genie (wish hermes-khaw-native-surface, Group 1).
7 read-only tools grounded on the v5 CLI (doctor/board/task list/task
status/launch --dry-run), argv-only subprocess bridge with shell-metachar
rejection, validate_ref traversal guard + in-bounds WISH.md read, uniform
{success, mutation:none, cwd, command|source} payload. 28 pytest cases.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01BEJCsZTjxLyrM8BQKjGEVs
…scripts, profile seed update

Group 3 of wish hermes-khaw-native-surface. Install script (symlink
default, --copy mode), smoke script, native-surface + mutation-gates
references, Hermes-native cross-links in root README, Claude Code plugin
README, and the Hermes profile seed (additive). Two review LOWs applied:
ln -sfn manual one-liner, payload-contract wording.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01BEJCsZTjxLyrM8BQKjGEVs
… CLI tree, skills

Group 2 of wish hermes-khaw-native-surface. /genie dispatcher (+4 wrapper
commands) with outcome-first rendering and evidence footers, advisory
hooks (session-start .genie reminder, terminal-scrape advice, never
blocking), hasattr-guarded CLI tree and 4 path-based skills. 18 new tests
(46 total). Two review LOWs applied: on_session_start degrades on
unresolvable cwd; list-shaped command events joined before advisory match.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01BEJCsZTjxLyrM8BQKjGEVs
…sh skills-fable5-revamp G1)

803->48 and 627->47 lines; optimizer prompt extracted verbatim to
prompts/optimizer.md (runtime-read at dispatch); hack catalog moved to
references/ with all 8 hacks preserved and re-grounded in the live v5 CLI;
skills-lint:ignore marker removed, lint green.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_014SwFHxrsQVdwG32ChnsKku
Wish skills-fable5-revamp. 396->108, 196->103, 259->107, 282->101;
daemon-era team/agent/events flows rewritten to native-team dispatch
(Agent tool + SendMessage) and the task DB; grounded-progress clauses
added to pm/dream/report; mode contracts extracted to references/;
5 lint markers removed, lint green.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_014SwFHxrsQVdwG32ChnsKku
Wish skills-fable5-revamp. brainstorm 230->125, wish 105->71,
work 181->103, review 171->114, fix 112->75, trace 109->54; all 8
frozen handoff contracts preserved verbatim (template cp rule,
wishes:lint gate, task linkage, verdict vocabulary, artifact paths,
reviewer!=engineer, session-close outcome words, native-team dispatch);
2 lint markers removed and refs fixed at the root; lint green.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_014SwFHxrsQVdwG32ChnsKku
Wish skills-fable5-revamp. genie 186->94 (25 intents -> 27 routes, zero
dropped), wizard 161->57, learn 108->61, docs 83->43, omni 164->74;
lifecycle.md re-grounded to v5 zero-daemon; genie-omni wiring rebuilt
from source (omni-config.ts routes contract, ed25519 handshake); two
baseline factual errors fixed (orchestration-guard nudges not blocks,
genie task status exists); 5 lint markers removed, lint green.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_014SwFHxrsQVdwG32ChnsKku
…urface

feat: Hermes-native plugin surface for Genie

@coderabbitai coderabbitai Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

Actionable comments posted: 3

🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

Inline comments:
In `@plugins/hermes-genie/references/native-surface.md`:
- Line 3: The pinned Genie CLI version in the Hermes native-surface reference is
out of date. Update the version mention in native-surface.md so it matches the
new repo/plugin release (the note currently references genie 5.260703.5, but the
PR updates to 5.260704.2); keep the wording aligned with the existing “Grounded
against the genie v5 CLI” note and adjust only the version string in that
documentation block.

In `@plugins/hermes-genie/scripts/install-local.sh`:
- Around line 44-47: The install-local.sh cleanup step unconditionally removes
$target, which can wipe an unrelated real directory instead of only replacing a
previous plugin link. Update the logic around the mkdir/rm block to detect
whether $target is an expected symlink or install-owned plugin directory before
deleting it, and refuse or warn when it points to pre-existing non-plugin
content. Make sure the guard is handled in the path that prepares $target and is
adjusted for the --copy mode behavior where a real directory is intentionally
recreated.

In `@profiles/hermes/genie/README.md`:
- Around line 11-19: The bootstrap seed list and copy step are inconsistent:
`CLAUDE_CODE_PILOT.md` is documented as included but is not actually copied, and
the `hermes profile create` path is not fail-fast because `|| true` suppresses
errors. Update the bootstrap instructions in `README.md` so the file copy step
includes `CLAUDE_CODE_PILOT.md`, and remove the error suppression so failures
from `hermes profile create` surface immediately; keep the wording aligned with
the seed list and the bootstrap section that describes the profile creation
flow.
🪄 Autofix (Beta)

Fix all unresolved CodeRabbit comments on this PR:

  • Push a commit to this branch (recommended)
  • Create a new PR with the fixes

ℹ️ Review info
⚙️ Run configuration

Configuration used: Path: .coderabbit.yaml

Review profile: ASSERTIVE

Plan: Pro

Run ID: 9fc7409c-4735-4ceb-939d-c3767cf63f90

📥 Commits

Reviewing files that changed from the base of the PR and between 6301567 and 703c18a.

⛔ Files ignored due to path filters (1)
  • README.md is excluded by !*.md
📒 Files selected for processing (32)
  • .claude-plugin/marketplace.json
  • .genie/wishes/hermes-khaw-native-surface/WISH.md
  • package.json
  • plugins/genie/.claude-plugin/plugin.json
  • plugins/genie/README.md
  • plugins/genie/package.json
  • plugins/hermes-genie/.gitignore
  • plugins/hermes-genie/README.md
  • plugins/hermes-genie/__init__.py
  • plugins/hermes-genie/commands.py
  • plugins/hermes-genie/genie_bridge.py
  • plugins/hermes-genie/hooks.py
  • plugins/hermes-genie/plugin.yaml
  • plugins/hermes-genie/references/mutation-gates.md
  • plugins/hermes-genie/references/native-surface.md
  • plugins/hermes-genie/schemas.py
  • plugins/hermes-genie/scripts/install-local.sh
  • plugins/hermes-genie/scripts/smoke.sh
  • plugins/hermes-genie/skills/genie-khaw-bridge/SKILL.md
  • plugins/hermes-genie/skills/genie-review/SKILL.md
  • plugins/hermes-genie/skills/genie-work/SKILL.md
  • plugins/hermes-genie/skills/genie/SKILL.md
  • plugins/hermes-genie/tests/test_commands.py
  • plugins/hermes-genie/tests/test_genie_bridge.py
  • plugins/hermes-genie/tests/test_plugin_contract.py
  • profiles/hermes/genie/AGENTS.md
  • profiles/hermes/genie/CLAUDE_CODE_PILOT.md
  • profiles/hermes/genie/HEARTBEAT.md
  • profiles/hermes/genie/README.md
  • profiles/hermes/genie/SOUL.md
  • profiles/hermes/genie/agent.yaml
  • profiles/hermes/genie/hermes-profile.yaml

@@ -0,0 +1,63 @@
# Native Surface Reference

What the Genie Hermes plugin exposes, layer by layer, and the exact contract every layer honors. Grounded against the genie v5 CLI (flags verified 2026-07-04 on genie 5.260703.5).

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

📐 Maintainability & Code Quality | 🟡 Minor | ⚡ Quick win

🧩 Analysis chain

🏁 Script executed:

#!/bin/bash
rg -n '5\.26070' package.json plugins/genie/package.json plugins/genie/.claude-plugin/plugin.json .claude-plugin/marketplace.json plugins/hermes-genie/references/native-surface.md

Repository: automagik-dev/genie

Length of output: 623


🏁 Script executed:

#!/bin/bash
set -euo pipefail

printf '\n== Version fields ==\n'
sed -n '1,40p' package.json
printf '\n== plugins/genie/package.json ==\n'
sed -n '1,40p' plugins/genie/package.json
printf '\n== plugins/genie/.claude-plugin/plugin.json ==\n'
sed -n '1,60p' plugins/genie/.claude-plugin/plugin.json
printf '\n== .claude-plugin/marketplace.json ==\n'
sed -n '1,80p' .claude-plugin/marketplace.json

printf '\n== References to 5.260703.5 / 5.260704.2 ==\n'
rg -n '5\.260703\.5|5\.260704\.2|genie v5 CLI|flags verified 2026-07-04' .

Repository: automagik-dev/genie

Length of output: 4203


Update the pinned genie CLI version in this note. plugins/hermes-genie/references/native-surface.md:3 still cites genie 5.260703.5, while this PR bumps the repo/plugin version to 5.260704.2; align the reference so it doesn’t drift on merge.

🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@plugins/hermes-genie/references/native-surface.md` at line 3, The pinned
Genie CLI version in the Hermes native-surface reference is out of date. Update
the version mention in native-surface.md so it matches the new repo/plugin
release (the note currently references genie 5.260703.5, but the PR updates to
5.260704.2); keep the wording aligned with the existing “Grounded against the
genie v5 CLI” note and adjust only the version string in that documentation
block.

Comment thread plugins/hermes-genie/scripts/install-local.sh Outdated
Comment thread profiles/hermes/genie/README.md
@namastex888

Copy link
Copy Markdown
Contributor Author

Independent review record (pre-merge)

Two independent code reviews were commissioned on the full dev→main diff (per promotion protocol), plus adversarial verification of all 6 existing bot findings.

Reviewer A (correctness/security): MERGE-WITH-FOLLOW-UPS. All 6 bot findings verified against actual code — none refuted, none release-blocking. Priority follow-ups (all in omni-serve Model A, opt-in config.routes feature, no existing-consumer breakage):

  1. src/lib/omni-runner.ts:997 — startRoutedRun fires before any reaction check: an emoji reaction in a mapped chat spawns a spurious claude run and publishes a reply, acking the reacted-to message (amplifies codex P2; fix-first if routes go live in prod)
  2. omni-runner.ts:~709/734 — route ⏳→✅ acks are unordered fire-and-forget with no reconciliation path for route (non-approval) reactions
  3. omni-runner.ts:~155 — add -- before the positional message (hyphen-leading messages parse as flags; not an injection, single-argv)
  4. omni-runner.ts:~268/731 — non-zero-exit notice drops stdout, and toSpawnResult discards stderr entirely (operator never sees claude's real error)
  5. afterEach tmpdir consolidation in omni-runner.test.ts (trivial, test-only)

Security pass on plugins/hermes-genie/ (Python): clean — argv-only subprocess, metachar rejection, validate_ref traversal guard + symlink-escape defense all hold under live exploit attempts. Tests: omni-runner 45/45, plugin 46/46, typecheck clean, full bun test 655 pass + 2 documented pre-existing environmental failures (host ~/.genie/config.json leaking via hardcoded homedir() in src/lib/genie-config.ts — fails on any branch on that host).

Reviewer B (release-safety/merge-integrity): MERGE. Proven: exactly one conflict (profiles/hermes/genie/README.md add/add; dev's blob is a byte-exact superset of main's, +10/−0), zero reversions across all 31 changed paths (main's advanced .well-known manifests untouched; package.json moves forward only), CI on main stays green (biome/knip/tsconfig/bun-test scopes exclude the Python plugin; wishes-lint passes), no new runtime deps/binaries/secrets. Note: merging re-dispatches release against v5.260704.x stable channel — the designed pipeline.

Merge plan: manual merge of dev into main resolving the single README conflict to dev's superset blob (keeps this PR's stated no-resync contract — nothing back-merged into dev). merge-tree re-verified at live tips before pushing.

🤖 Generated with Claude Code

https://claude.ai/code/session_01BEJCsZTjxLyrM8BQKjGEVs

namastex888 and others added 3 commits July 4, 2026 19:35
Performs the dev<->main resync deferred in #2516 so the rolling PR merges
conflict-free. Sole conflict (profiles/hermes/genie/README.md add/add)
resolved to dev's blob — a byte-exact superset of main's seed (+10 lines,
proven by independent merge-integrity review on #2516).

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01BEJCsZTjxLyrM8BQKjGEVs

# Conflicts:
#	profiles/hermes/genie/README.md
- native-surface.md: version-agnostic grounding line (contract tests pin
  the flag surface) instead of a stale exact-version pin
- install-local.sh: guard rm -rf — only replace a symlink or a dir that
  looks like a previous plugin install (plugin.yaml present); refuse and
  exit 1 otherwise (verified: refusal preserves unrelated content)
- profiles/hermes/genie/README.md (pre-existing seed content surfaced by
  the add/add resolution): bootstrap copies CLAUDE_CODE_PILOT.md as
  documented and no longer hides 'hermes profile create' failures

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01BEJCsZTjxLyrM8BQKjGEVs
@namastex888

Copy link
Copy Markdown
Contributor Author

Ready for human merge (§19)

All three CodeRabbit actionable comments are addressed in ab4c8a8:

  1. native-surface.md — version-agnostic grounding line (the flag surface is pinned by the plugin's contract tests, so the doc no longer carries a stale exact-version pin)
  2. install-local.sh — rm -rf is now guarded: it only replaces a symlink or a directory that looks like a previous plugin install (plugin.yaml present), otherwise refuses with exit 1. Verified: refusal path preserves unrelated content
  3. profiles/hermes/genie/README.md — bootstrap now copies CLAUDE_CODE_PILOT.md as documented and no longer masks hermes profile create failures. (This was pre-existing seed content surfaced as new by the add/add resolution, fixed anyway since it was two lines)

The earlier codex/gemini re-reviews (19:53Z) restate the omni-runner findings already triaged in the independent-review record above — follow-ups, none blocking.

State: MERGEABLE / CLEAN, all checks green. The dev↔main resync deferred in the PR description is done (a547f51) — this merge now only adds dev's commits to main, and dev already contains main. Agent policy (§19) reserves main merges for humans, so this PR now awaits the button. Reminder from the release-safety review: merging re-dispatches the release pipeline toward the stable channel — the designed consequence of promoting to main.

Full trail: wish hermes-khaw-native-surface (5/5 groups, per-group independent reviews), #2517 (wish→dev, 11/11 CI), two independent promotion reviews + 6 bot findings adversarially verified (comment above), live dogfood evidence in the KHAW repo (docs/evidence/genie-hermes-khaw-native-surface-smoke-2026-07-04.md).

🤖 Generated with Claude Code

https://claude.ai/code/session_01BEJCsZTjxLyrM8BQKjGEVs

namastex888 and others added 3 commits July 4, 2026 19:47
Wish skills-fable5-revamp. New legacy-v4 module: shared path manifest,
detectV4Install(), exported cleanupV4() — content-marker gated rules-file
removal (backup-first to ~/.genie/state-backups/v4-cleanup-<ts>/, logged,
idempotent), orphaned automagik/genie/4.* cache cleanup (.orphaned_at
gated, manifest backup). Wired into a recreated thin 'genie install'
finisher (bootstrap handoff restored, non-exec, --skip-v4-cleanup) and
guarded post-delivery in 'genie update' so upgraded machines clean too.
uninstall.ts consumes the shared manifest. 16 pgserve-free tests incl.
backup-before-delete and degradation locks; wish doc re-grounded to dev
reality; v4-footprint inventory in reports/.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_014SwFHxrsQVdwG32ChnsKku
All 10 Success Criteria PASS with pasted evidence: combined surface
5514 -> 2016 lines (-63.4%), skills:lint 118 dead refs -> 0 with zero
ignore markers, budgets held, frozen contracts quoted, zero D/R in
either repo diff, G8 gates green. Conventions live-CLI list gains
'install' (recreated by G8).

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_014SwFHxrsQVdwG32ChnsKku
CI runners don't ship the omni binary; the hard exit(2) in
getOmniCommands() was masked pre-revamp because every omni-referencing
skill hid behind a skills-lint:ignore marker. Default now warns and
skips ONLY omni validation (genie checks stay strict) with an honest
'omni checks skipped' suffix; SKILLS_LINT_REQUIRE_OMNI=1 restores the
strict behavior. Wish skills-fable5-revamp (adjudicated PR-gate fix).

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_014SwFHxrsQVdwG32ChnsKku
Wish skills-fable5-revamp — genie skills + v4 trash cleaning. All checks green after skills-lint graceful-degradation fix; 7 independent group reviews SHIP; G7 verification all-10-SC PASS (.genie/wishes/skills-fable5-revamp/verification.md)
@namastex888

Copy link
Copy Markdown
Contributor Author

Now included in this promotion: wish skills-fable5-revamp (PR #2518, merged to dev).

  • All 17 skills revamped to the Fable 5 standard: genie skill surface 4,185 → 1,385 lines; 118 dead pre-v5 CLI references fixed at the root; all 13 skills-lint:ignore markers removed (lint passes on merit, with graceful omni-probe degradation for CI).
  • v4 trash-cleaning shipped (legacy-v4.ts): genie install/genie update now detect and clean the stale ~/.claude/rules/genie-orchestration.md + orphaned 4.x plugin caches — marker-gated, backup-first, idempotent. Already field-verified on a real v4-upgraded machine (removed 1 rules file + 2 orphaned caches, backups + log confirmed, re-run no-op).
  • Evidence: .genie/wishes/skills-fable5-revamp/verification.md (all 10 success criteria, −63.4% combined surface). Companion omni promotion: chore: rolling promotion dev -> main omni#779.

🤖 Generated with Claude Code

@namastex888
namastex888 merged commit 5f5b240 into main Jul 4, 2026
16 checks passed
namastex888 added a commit that referenced this pull request Jul 5, 2026
Closes the three findings confirmed by the independent reviews on #2516:

- Reaction frames (and blank bodies) in a route-mapped chat no longer
  spawn a claude run, publish a reply, or mutate the reacted-to message's
  status ack — they are stored to the inbox only. The approval-chat
  reaction path is untouched, including when the route chat IS the
  approval chat (new coexistence test).
- buildClaudeArgs inserts '--' before the message positional so a
  hyphen-leading message is always a prompt, never parsed as a flag
  (verified live against the claude CLI).
- Non-zero exits now surface a bounded tail of the child's stderr
  (fallback stdout) in the error notice instead of a bare exit code;
  SpawnClaudeResult carries stderr through the existing drain pattern.

52 tests (45 pre-existing + 7 new), typecheck and biome clean.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01Sg8vJv9r2yqmnbtVPqM2vG
@coderabbitai coderabbitai Bot mentioned this pull request Jul 5, 2026
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