Skip to content
Merged

Dev #1010

Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletion .claude-plugin/marketplace.json
Original file line number Diff line number Diff line change
Expand Up @@ -10,7 +10,7 @@
"plugins": [
{
"name": "genie",
"version": "4.260402.20",
"version": "4.260403.2",
"source": "./plugins/genie",
"description": "Human-AI partnership for Claude Code. Share a terminal, orchestrate workers, evolve together. Brainstorm ideas, wish them into plans, make with parallel agents, ship as one team. A coding genie that grows with your project."
}
Expand Down
2 changes: 1 addition & 1 deletion package.json
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
{
"name": "@automagik/genie",
"version": "4.260402.20",
"version": "4.260403.2",
"description": "Collaborative terminal toolkit for human + AI workflows",
"type": "module",
"bin": {
Expand Down
2 changes: 1 addition & 1 deletion plugins/genie/.claude-plugin/plugin.json
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
{
"name": "genie",
"version": "4.260402.20",
"version": "4.260403.2",
"description": "Human-AI partnership for Claude Code. Share a terminal, orchestrate workers, evolve together. Brainstorm ideas, turn them into wishes, execute with /work, validate with /review, and ship as one team.",
"author": {
"name": "Namastex Labs"
Expand Down
3 changes: 1 addition & 2 deletions plugins/genie/agents/council--architect/AGENTS.md
Original file line number Diff line number Diff line change
Expand Up @@ -5,8 +5,7 @@ model: haiku
provider: claude
color: blue
promptMode: append
tools: ["Read", "Glob", "Grep"]
permissionMode: plan
tools: ["Read", "Glob", "Grep", "Bash"]
---

@SOUL.md
Expand Down
3 changes: 1 addition & 2 deletions plugins/genie/agents/council--benchmarker/AGENTS.md
Original file line number Diff line number Diff line change
Expand Up @@ -5,8 +5,7 @@ model: haiku
provider: claude
color: orange
promptMode: append
tools: ["Read", "Glob", "Grep"]
permissionMode: plan
tools: ["Read", "Glob", "Grep", "Bash"]
---

@SOUL.md
Expand Down
3 changes: 1 addition & 2 deletions plugins/genie/agents/council--deployer/AGENTS.md
Original file line number Diff line number Diff line change
Expand Up @@ -5,8 +5,7 @@ model: haiku
provider: claude
color: green
promptMode: append
tools: ["Read", "Glob", "Grep"]
permissionMode: plan
tools: ["Read", "Glob", "Grep", "Bash"]

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

⚠️ Potential issue | 🔴 Critical

Adding Bash requires explicit non-permissive permission mode.

Line 8 is fine only if permission behavior is pinned. Right now permissionMode is absent, which can make this agent run too permissively.

As per coding guidelines plugins/**: Claude Code plugin. Verify plugin manifest updates are consistent.

🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@plugins/genie/agents/council--deployer/AGENTS.md` at line 8, The tools list
for this agent includes "Bash" but no permissionMode is declared, making
execution potentially too permissive; update the agent's manifest that contains
the tools entry (the block with tools: ["Read", "Glob", "Grep", "Bash"]) to add
an explicit non‑permissive permissionMode (e.g., permissionMode: "explicit") and
enumerate only the allowed tools/permissions for this agent; ensure the plugin
manifest under the same plugins/** bundle is updated consistently with the
Claude Code plugin guidelines so Bash use is restricted to the declared explicit
permissions.

---

@SOUL.md
Expand Down
3 changes: 1 addition & 2 deletions plugins/genie/agents/council--ergonomist/AGENTS.md
Original file line number Diff line number Diff line change
Expand Up @@ -5,8 +5,7 @@ model: haiku
provider: claude
color: cyan
promptMode: append
tools: ["Read", "Glob", "Grep"]
permissionMode: plan
tools: ["Read", "Glob", "Grep", "Bash"]

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

⚠️ Potential issue | 🔴 Critical

Permission mode must stay explicit after adding Bash.

Line 8 adds Bash; without explicit permissionMode, this can unintentionally broaden execution permissions. Re-add permissionMode: plan.

As per coding guidelines plugins/**: Claude Code plugin. Verify plugin manifest updates are consistent.

🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@plugins/genie/agents/council--ergonomist/AGENTS.md` at line 8, The manifest
entry that now includes "Bash" in the tools array (tools: ["Read", "Glob",
"Grep", "Bash"]) must explicitly retain a permissionMode to avoid widening
execution privileges; update the AGENTS.md/plugin manifest so that alongside the
tools array you add permissionMode: plan (e.g., ensure the same block containing
tools also contains permissionMode: plan) and verify the change follows the
plugins/** Claude Code plugin manifest conventions for consistency.

---

@SOUL.md
Expand Down
3 changes: 1 addition & 2 deletions plugins/genie/agents/council--measurer/AGENTS.md
Original file line number Diff line number Diff line change
Expand Up @@ -5,8 +5,7 @@ model: haiku
provider: claude
color: yellow
promptMode: append
tools: ["Read", "Glob", "Grep"]
permissionMode: plan
tools: ["Read", "Glob", "Grep", "Bash"]
---

@SOUL.md
Expand Down
3 changes: 1 addition & 2 deletions plugins/genie/agents/council--operator/AGENTS.md
Original file line number Diff line number Diff line change
Expand Up @@ -5,8 +5,7 @@ model: haiku
provider: claude
color: red
promptMode: append
tools: ["Read", "Glob", "Grep"]
permissionMode: plan
tools: ["Read", "Glob", "Grep", "Bash"]

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

⚠️ Potential issue | 🔴 Critical

Bash enablement needs explicit permission guard.

Line 8 enables Bash, but permissionMode is missing; this can default to a permissive mode at spawn time. Keep permissionMode: plan explicit.

As per coding guidelines plugins/**: Claude Code plugin. Verify plugin manifest updates are consistent.

🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@plugins/genie/agents/council--operator/AGENTS.md` at line 8, The manifest
currently lists tools: ["Read","Glob","Grep","Bash"] with Bash enabled but
missing the permissionMode guard; update the AGENTS.md plugin manifest entry
that defines tools to explicitly add permissionMode: "plan" for the Bash tool
(i.e., ensure the Bash tool declaration includes permissionMode: plan), and
verify the corresponding plugin manifest under the plugins/** Claude Code plugin
is updated consistently so the spawn-time permissions are explicit and not
defaulting to permissive.

---

@SOUL.md
Expand Down
3 changes: 1 addition & 2 deletions plugins/genie/agents/council--questioner/AGENTS.md
Original file line number Diff line number Diff line change
Expand Up @@ -5,8 +5,7 @@ model: haiku
provider: claude
color: magenta
promptMode: append
tools: ["Read", "Glob", "Grep"]
permissionMode: plan
tools: ["Read", "Glob", "Grep", "Bash"]

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

⚠️ Potential issue | 🔴 Critical

Line 8 introduces risk without explicit permission pinning.

Bash is enabled, but permissionMode is no longer explicit. Please restore permissionMode: plan to prevent unintended permissive execution.

As per coding guidelines plugins/**: Claude Code plugin. Verify plugin manifest updates are consistent.

🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@plugins/genie/agents/council--questioner/AGENTS.md` at line 8, The AGENTS.md
entry enabling tools includes "Bash" but removed an explicit permissionMode;
restore permissionMode: plan alongside the tools declaration to enforce
approval-before-execution (update the same AGENTS.md block where tools: ["Read",
"Glob", "Grep", "Bash"] appears), and ensure the corresponding plugin manifest
under plugins/** (the Claude Code plugin entries) is updated to match this
permissionMode change so the runtime uses plan-mode for Bash invocations.

---

@SOUL.md
Expand Down
3 changes: 1 addition & 2 deletions plugins/genie/agents/council--sentinel/AGENTS.md
Original file line number Diff line number Diff line change
Expand Up @@ -5,8 +5,7 @@ model: haiku
provider: claude
color: red
promptMode: append
tools: ["Read", "Glob", "Grep"]
permissionMode: plan
tools: ["Read", "Glob", "Grep", "Bash"]

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

⚠️ Potential issue | 🔴 Critical

Security regression: explicit permission mode is now missing.

Line 8 adds Bash, but without an explicit permissionMode this agent can fall back to a permissive runtime mode (bypassPermissions), which is risky for a security-focused agent.

Suggested fix
 tools: ["Read", "Glob", "Grep", "Bash"]
+permissionMode: plan

As per coding guidelines plugins/**: Claude Code plugin. Verify plugin manifest updates are consistent.

📝 Committable suggestion

‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.

Suggested change
tools: ["Read", "Glob", "Grep", "Bash"]
tools: ["Read", "Glob", "Grep", "Bash"]
permissionMode: plan
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@plugins/genie/agents/council--sentinel/AGENTS.md` at line 8, The tools list
was extended to include "Bash" but omitted an explicit permissionMode, which can
allow fallback to a permissive runtime like bypassPermissions; update the agent
manifest/AGENTS.md entry so that the tools array (the "tools" key) includes a
corresponding "permissionMode" field set to a restrictive value (e.g.,
"restricted" or "allow-listed") for this agent, explicitly declare
permissionMode alongside the "Bash" tool entry, and ensure the manifest follows
the plugins/** guidelines for Claude Code plugins so that bypassPermissions is
not implicitly enabled.

---

@SOUL.md
Expand Down
3 changes: 1 addition & 2 deletions plugins/genie/agents/council--simplifier/AGENTS.md
Original file line number Diff line number Diff line change
Expand Up @@ -5,8 +5,7 @@ model: haiku
provider: claude
color: green
promptMode: append
tools: ["Read", "Glob", "Grep"]
permissionMode: plan
tools: ["Read", "Glob", "Grep", "Bash"]
---

@SOUL.md
Expand Down
3 changes: 1 addition & 2 deletions plugins/genie/agents/council--tracer/AGENTS.md
Original file line number Diff line number Diff line change
Expand Up @@ -5,8 +5,7 @@ model: haiku
provider: claude
color: cyan
promptMode: append
tools: ["Read", "Glob", "Grep"]
permissionMode: plan
tools: ["Read", "Glob", "Grep", "Bash"]

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

⚠️ Potential issue | 🔴 Critical

Expanded tool capability without explicit permission control.

Line 8 adds Bash, but frontmatter no longer pins permissionMode. That combination can materially weaken runtime safety. Keep permission mode explicit in this manifest.

As per coding guidelines plugins/**: Claude Code plugin. Verify plugin manifest updates are consistent.

🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@plugins/genie/agents/council--tracer/AGENTS.md` at line 8, The tools list now
includes "Bash" but the manifest frontmatter no longer sets permissionMode;
restore an explicit permissionMode entry in the AGENTS.md frontmatter (e.g.,
permissionMode: "restricted" or the project's standard value) to make the
runtime permission policy explicit, and ensure the tools array (tools:
["Read","Glob","Grep","Bash"]) remains unchanged; verify the updated manifest
follows the plugins/** Claude Code plugin guidelines for permissionMode and tool
exposure consistency.

---

@SOUL.md
Expand Down
31 changes: 29 additions & 2 deletions plugins/genie/agents/council/AGENTS.md
Original file line number Diff line number Diff line change
Expand Up @@ -5,8 +5,7 @@ model: haiku
provider: claude
color: purple
promptMode: append
tools: ["Read", "Glob", "Grep"]
permissionMode: plan
tools: ["Read", "Glob", "Grep", "Bash"]
---

@SOUL.md
Expand All @@ -17,6 +16,32 @@ Orchestrate real multi-agent deliberation by spawning council members via genie
Architectural decisions are expensive to reverse. Shallow review misses failure modes. Real multi-agent deliberation with distinct reasoning chains catches what single viewpoints miss.
</mission>

<spawning>
Council members MUST be spawned via `genie spawn` — this routes through genie's tmux topology management and places members in the correct session/window.

**Spawn each selected member:**
```bash
genie spawn council--<member> --team $GENIE_TEAM
```

**NEVER use the Agent tool to spawn council members.** The Agent tool creates a separate tmux session, breaking the session topology. All spawning goes through `genie spawn`.

**Post topic to team chat after spawning:**
```bash
genie broadcast "COUNCIL TOPIC: <topic>" --team $GENIE_TEAM
```

**Send instructions to members:**
```bash
genie send "<instructions>" --to council--<member> --team $GENIE_TEAM
```
Comment on lines +20 to +37

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

⚠️ Potential issue | 🟠 Major

Use actual CLI command signatures in orchestration examples.

Lines 20-37 document genie spawn, genie broadcast, and genie send, but the referenced command implementations expose genie agent spawn and genie agent send (with --broadcast). This will break copy/paste execution.

Proposed doc fix
-Council members MUST be spawned via `genie spawn` — this routes through genie's tmux topology management and places members in the correct session/window.
+Council members MUST be spawned via `genie agent spawn` — this routes through genie's tmux topology management and places members in the correct session/window.

 ```bash
-genie spawn council--<member> --team $GENIE_TEAM
+genie agent spawn council--<member> --team $GENIE_TEAM

-NEVER use the Agent tool to spawn council members. The Agent tool creates a separate tmux session, breaking the session topology. All spawning goes through genie spawn.
+NEVER use the Agent tool to spawn council members. The Agent tool creates a separate tmux session, breaking the session topology. All spawning goes through genie agent spawn.

-genie broadcast "COUNCIL TOPIC: <topic>" --team $GENIE_TEAM
+genie agent send "COUNCIL TOPIC: <topic>" --broadcast --team $GENIE_TEAM
-genie send "<instructions>" --to council--<member> --team $GENIE_TEAM
+genie agent send "<instructions>" --to council--<member> --team $GENIE_TEAM
</details>

<details>
<summary>🤖 Prompt for AI Agents</summary>

Verify each finding against the current code and only fix it if needed.

In @plugins/genie/agents/council/AGENTS.md around lines 20 - 37, The examples
and guidance reference non-existent commands (genie spawn, genie broadcast,
genie send) and should be updated to the real CLI verbs: use genie agent spawn to spawn council members, replace the sentence "All spawning goes through
genie spawn" with "All spawning goes through genie agent spawn", use genie agent send "<topic>" --broadcast --team $GENIE_TEAM for team broadcasts, and
use genie agent send "<instructions>" --to council--<member> --team $GENIE_TEAM for per-member messages; ensure --broadcast is included where the
doc previously said genie broadcast.


</details>

<!-- fingerprinting:phantom:triton:hawk:82119aad-e16c-45a7-bfb1-3ebe794f31c2 -->

<!-- This is an auto-generated comment by CodeRabbit -->


**Read team chat for responses:**
```bash
genie chat read <convId>
```
</spawning>

<routing>
Not every topic needs all 10 perspectives. Route based on topic:

Expand Down Expand Up @@ -70,4 +95,6 @@ The council produces a structured report with:
- Always synthesize — raw perspectives without interpretation are not useful
- No voting — no APPROVE/REJECT/MODIFY verdicts. The council thinks; `/review` judges.
- Dissent is preserved — minority views are captured, never suppressed
- **NEVER use the Agent tool to spawn members** — always `genie spawn`
- **NEVER create teams** — use the team you were spawned into (`$GENIE_TEAM`)
</constraints>
1 change: 1 addition & 0 deletions plugins/genie/agents/team-lead/AGENTS.md
Original file line number Diff line number Diff line change
Expand Up @@ -118,6 +118,7 @@ Specialist spawns are ADDITIONS to the default flow (except refactor replacing e
- NEVER write code. `genie work` dispatches engineers who write code.
- NEVER push to main or master.
- NEVER use the Agent tool — use `genie work` to dispatch.
- NEVER pass `--session` to `genie spawn` — the team config resolves the correct tmux session automatically. Passing `--session <team>` creates a separate session, breaking topology.

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

⚠️ Potential issue | 🟠 Major

🧩 Analysis chain

🏁 Script executed:

#!/bin/bash
# Check spawn command definition and validation logic

echo "=== Spawn command accepts both --team and --session ==="
rg -n -A 2 "\.option.*--session" src/term-commands/agent/spawn.ts

echo ""
echo "=== handleWorkerSpawn sets sessionOverride without validation ==="
rg -n -C 3 "sessionOverride.*options\.session" src/term-commands/agents.ts

echo ""
echo "=== Search for any validation that rejects both flags together ==="
rg -n "session.*team|team.*session" src/term-commands/agents.ts | rg -i "error|throw|reject"

Repository: automagik-dev/genie

Length of output: 909


🏁 Script executed:

cat -n plugins/genie/agents/team-lead/AGENTS.md | sed -n '115,130p'

Repository: automagik-dev/genie

Length of output: 764


🏁 Script executed:

#!/bin/bash
# Get context around handleWorkerSpawn to understand what it does with --team and --session
echo "=== handleWorkerSpawn implementation context ==="
rg -n -B 10 -A 15 "async function handleWorkerSpawn" src/term-commands/agents.ts | head -60

Repository: automagik-dev/genie

Length of output: 1346


🏁 Script executed:

#!/bin/bash
# Check if there's validation that prevents --team and --session together
echo "=== Check for validation before handleWorkerSpawn call ==="
rg -n -B 15 "handleWorkerSpawn" src/term-commands/agents.ts | grep -A 15 "team\|session" | head -40

Repository: automagik-dev/genie

Length of output: 121


🏁 Script executed:

#!/bin/bash
# Find where sessionOverride is used
echo "=== Where is sessionOverride used? ==="
rg -n "sessionOverride" src/term-commands/agents.ts | head -20

echo ""
echo "=== Check the spawn function that uses these params ==="
rg -n "function.*spawn\|export.*spawn" src/genie-commands/ | grep -v "AGENTS.md"

Repository: automagik-dev/genie

Length of output: 689


🏁 Script executed:

#!/bin/bash
# Check what doSpawn or spawn function does with sessionOverride
echo "=== Search for spawn function that accepts sessionOverride param ==="
rg -n "sessionOverride" src/genie-commands/ -A 2 -B 2

Repository: automagik-dev/genie

Length of output: 131


🏁 Script executed:

#!/bin/bash
# Examine the complete flow - find where options are parsed and passed to handleWorkerSpawn
echo "=== Command definition for spawn in agents.ts ==="
rg -n "\.command.*spawn\|\.subcommand.*spawn" src/term-commands/agents.ts -A 30 | head -60

echo ""
echo "=== Look for validation of team vs session flags ==="
rg -n "options\\.team\|options\\.session" src/term-commands/agents.ts | head -20

Repository: automagik-dev/genie

Length of output: 166


🏁 Script executed:

#!/bin/bash
# Check if there's any conditional logic that prevents both flags
echo "=== Check if options.team blocks options.session or vice versa ==="
rg -n "if.*team\|if.*session" src/term-commands/agents.ts -A 3 -B 1 | grep -A 3 -B 1 "team.*session\|session.*team"

echo ""
echo "=== View the actual spawn command definition ==="
rg -n "new Command\|spawn\(\)" src/term-commands/agents.ts | head -10

Repository: automagik-dev/genie

Length of output: 179


🏁 Script executed:

#!/bin/bash
# Find where the spawn command is registered
echo "=== Find spawn command registration ==="
fd -t f "agents.ts" src/term-commands/ -exec grep -l "spawn" {} \;

echo ""
echo "=== Look at the whole agents.ts file structure ==="
wc -l src/term-commands/agents.ts

echo ""
echo "=== Search for Command or command near spawn ==="
rg -n "Command|\.command|\.action" src/term-commands/agents.ts | grep -i spawn

Repository: automagik-dev/genie

Length of output: 521


🏁 Script executed:

#!/bin/bash
# Let's find spawn.ts directly
echo "=== Find spawn.ts file ==="
fd -t f "spawn.ts" src/

echo ""
echo "=== Check spawn.ts for validation ==="
cat -n src/term-commands/agent/spawn.ts | head -80

Repository: automagik-dev/genie

Length of output: 2345


🏁 Script executed:

#!/bin/bash
# Final verification: confirm that sessionOverride completely bypasses team resolution
echo "=== Confirm sessionOverride priority in spawn execution ==="
rg -n -B 5 -A 5 "let sessionName = sessionOverride" src/term-commands/agents.ts

Repository: automagik-dev/genie

Length of output: 479


Add validation to reject --session flag when --team is provided.

The constraint documented in AGENTS.md (line 121) prohibits passing --session to genie spawn, warning that it breaks topology by overriding the team's configured session. However, handleWorkerSpawn in src/term-commands/agents.ts accepts both --team and --session without validation. At line 1280, sessionOverride is set directly from options.session, and at line 693 of src/term-commands/agents.ts, the override takes priority over the team's configured session name (lines 694–696). This allows users to violate the documented constraint and cause exactly the topology breakage it warns against.

Add validation in src/term-commands/agent/spawn.ts before calling handleWorkerSpawn to reject the combination of --team (or implicit team via GENIE_TEAM) and --session together.

🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@plugins/genie/agents/team-lead/AGENTS.md` at line 121, Add input validation
in the spawn CLI handler so that when a team is specified (either via the --team
option or implicitly via the GENIE_TEAM env var) the command rejects any
supplied --session flag; locate the spawn entry point in
src/term-commands/agent/spawn.ts and before calling handleWorkerSpawn, check for
options.session combined with options.team or process.env.GENIE_TEAM and return
an error/exit with a helpful message; this prevents the sessionOverride (set
from options.session and used by handleWorkerSpawn/sessionOverride) from taking
precedence over the team's configured session and enforces the rule documented
in AGENTS.md.

- NEVER poll faster than every 60 seconds. Always `sleep 60` before `genie status`.
- NEVER run more than 10 status checks per wave without progress.
- If `genie status` returns "No state found" → run `genie work <slug>` immediately.
Expand Down
2 changes: 1 addition & 1 deletion plugins/genie/package.json
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
{
"name": "genie-plugin",
"version": "4.260402.20",
"version": "4.260403.2",
"private": true,
"description": "Runtime dependencies for genie bundled CLIs",
"type": "module",
Expand Down
14 changes: 14 additions & 0 deletions plugins/genie/rules/genie-orchestration.md
Original file line number Diff line number Diff line change
Expand Up @@ -19,6 +19,20 @@ genie ls --json # Agent state from PG
NEVER use `Agent` to spawn agents — use `genie spawn` instead.
NEVER use `TeamCreate` or `TeamDelete` — use `genie team create` / `genie team disband` instead.

## Spawn Session Rule

NEVER pass `--session <team-name>` to `genie spawn`. The team config already stores the correct `tmuxSessionName` (resolved at team creation from the parent session). Passing `--session` overrides this and creates a separate tmux session, breaking the topology.

```bash
# WRONG — creates separate session
genie spawn reviewer --team my-team --session my-team

# CORRECT — uses team's configured session
genie spawn reviewer --team my-team
```

The `--session` flag is for rare manual overrides only. When `--team` is set, let genie resolve the session from the team config.
Comment on lines +22 to +34

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

⚠️ Potential issue | 🟠 Major

Rule clearly documented, but enforcement missing.

The "Spawn Session Rule" provides clear rationale and examples. However, as noted in the review of plugins/genie/agents/team-lead/AGENTS.md:121, the implementation in src/term-commands/agents.ts doesn't enforce this constraint - both --team and --session can be passed together, allowing the prohibited pattern.

Consider adding validation in handleWorkerSpawn to reject the combination or emit a warning.

🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@plugins/genie/rules/genie-orchestration.md` around lines 22 - 34, The spawn
rule is documented but not enforced: update the validation in handleWorkerSpawn
(in src/term-commands/agents.ts) to detect when both --team and --session are
supplied and reject or warn; specifically, if team is provided and session is
also passed, throw or return an error/warning instructing the caller to omit
--session (or instead resolve the tmuxSessionName from the team config and use
that value), include a clear message like "When --team is set, do not pass
--session; session will be resolved from team config" and ensure the handler
returns a failure code so the prohibited combination cannot proceed.


## Post-Dispatch Monitoring

After `genie team create` or `genie spawn`, use ONLY structured primitives. A hook enforces this automatically.
Expand Down
2 changes: 1 addition & 1 deletion skills/council/SKILL.md
Original file line number Diff line number Diff line change
Expand Up @@ -69,7 +69,7 @@ Execute all phases sequentially. YOU run every command, read every output, and m
Spawn each selected member. Use the double-dash naming convention (`council--<member>`):

```bash
genie spawn council--<member> --team <team> --session <team>
genie spawn council--<member> --team <team>
```

Run spawn commands in parallel (multiple Bash calls in one message). Read the output of each. If a spawn fails, note it and continue -- proceed as long as at least 2 members spawned successfully. If fewer than 2 succeed, clean up and report failure.
Expand Down
2 changes: 1 addition & 1 deletion skills/council/members/config.md
Original file line number Diff line number Diff line change
Expand Up @@ -23,7 +23,7 @@ Override per-session at spawn time:

```bash
# Use codex/o3 for the architect
genie spawn council--architect --team <team> --session <team> --provider codex --model o3
genie spawn council--architect --team <team> --provider codex --model o3

# Use haiku for all members (faster, cheaper)
# Pass --model haiku to the dispatch script
Expand Down
120 changes: 120 additions & 0 deletions src/term-commands/brain.test.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,120 @@
import { afterAll, beforeAll, beforeEach, describe, expect, test } from 'bun:test';
import { mkdirSync, rmSync, writeFileSync } from 'node:fs';
import { tmpdir } from 'node:os';
import { join } from 'node:path';
import { checkForUpdates, compareVersions } from './brain.js';

/**
* Tests for the cache-only checkForUpdates function.
*
* checkForUpdates reads a JSON cache file synchronously.
* It never makes network calls and never throws.
*
* We use a temp directory so tests never touch the real ~/.genie/
* and work in CI without permission issues.
*/

describe('checkForUpdates', () => {
let tempDir: string;
let cachePath: string;

beforeAll(() => {
tempDir = join(tmpdir(), `brain-test-${Date.now()}`);
mkdirSync(tempDir, { recursive: true });
cachePath = join(tempDir, 'brain-version-check.json');
});

afterAll(() => {
try {
rmSync(tempDir, { recursive: true, force: true });
} catch {
/* best effort */
}
});

beforeEach(() => {
try {
rmSync(cachePath);
} catch {
/* ok */
}
});
Comment on lines +17 to +41

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

🧹 Nitpick | 🔵 Trivial

Consider using afterEach for tmpdir cleanup per project conventions.

Per coding guidelines, test fixtures should use tmpdir with cleanup in afterEach. Currently, the temp directory is cleaned in afterAll, which means if a test fails, subsequent tests in the same suite run with potentially stale state. The beforeEach only cleans the cache file, not the entire temp directory.

This is minor since the current approach works, but afterEach would provide better test isolation.

♻️ Suggested adjustment
 describe('checkForUpdates', () => {
   let tempDir: string;
   let cachePath: string;

-  beforeAll(() => {
+  beforeEach(() => {
     tempDir = join(tmpdir(), `brain-test-${Date.now()}`);
     mkdirSync(tempDir, { recursive: true });
     cachePath = join(tempDir, 'brain-version-check.json');
   });

-  afterAll(() => {
+  afterEach(() => {
     try {
       rmSync(tempDir, { recursive: true, force: true });
     } catch {
       /* best effort */
     }
   });
-
-  beforeEach(() => {
-    try {
-      rmSync(cachePath);
-    } catch {
-      /* ok */
-    }
-  });

As per coding guidelines: "Use tmpdir with cleanup in afterEach for test fixtures"

📝 Committable suggestion

‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.

Suggested change
describe('checkForUpdates', () => {
let tempDir: string;
let cachePath: string;
beforeAll(() => {
tempDir = join(tmpdir(), `brain-test-${Date.now()}`);
mkdirSync(tempDir, { recursive: true });
cachePath = join(tempDir, 'brain-version-check.json');
});
afterAll(() => {
try {
rmSync(tempDir, { recursive: true, force: true });
} catch {
/* best effort */
}
});
beforeEach(() => {
try {
rmSync(cachePath);
} catch {
/* ok */
}
});
describe('checkForUpdates', () => {
let tempDir: string;
let cachePath: string;
beforeEach(() => {
tempDir = join(tmpdir(), `brain-test-${Date.now()}`);
mkdirSync(tempDir, { recursive: true });
cachePath = join(tempDir, 'brain-version-check.json');
});
afterEach(() => {
try {
rmSync(tempDir, { recursive: true, force: true });
} catch {
/* best effort */
}
});
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@src/term-commands/brain.test.ts` around lines 17 - 41, The test suite creates
tempDir in beforeAll and removes it in afterAll which risks stale state between
tests; change the fixture to create the temp directory in beforeEach (set
tempDir and cachePath there) and move the rmSync cleanup into afterEach (remove
tempDir recursively and force) so each test runs with a fresh tmpdir; update the
beforeEach that currently only removes cachePath to either rely on the new
per-test tmpdir or ensure it only manipulates cachePath within the test-specific
tempDir.


test('returns updateAvailable false when cache does not exist', () => {
const result = checkForUpdates(cachePath);
expect(result).toEqual({ updateAvailable: false });
});

test('returns updateAvailable true when cache says so', () => {
writeFileSync(
cachePath,
JSON.stringify({
checkedAt: new Date().toISOString(),
localVersion: '1.260401.1',
latestTag: 'v0.260403.2',
latestVersion: '0.260403.2',
updateAvailable: true,
}),
);
const result = checkForUpdates(cachePath);
expect(result).toEqual({ updateAvailable: true, latestVersion: '0.260403.2' });
});

test('returns updateAvailable false when cache says no update', () => {
writeFileSync(
cachePath,
JSON.stringify({
checkedAt: new Date().toISOString(),
localVersion: '1.260403.2',
latestTag: 'v0.260403.2',
latestVersion: '0.260403.2',
updateAvailable: false,
}),
);
const result = checkForUpdates(cachePath);
expect(result).toEqual({ updateAvailable: false });
});

test('returns updateAvailable false when cache is invalid JSON', () => {
writeFileSync(cachePath, 'not valid json{{{');
const result = checkForUpdates(cachePath);
expect(result).toEqual({ updateAvailable: false });
});

test('never throws regardless of cache content', () => {
writeFileSync(cachePath, JSON.stringify({ random: 'garbage', updateAvailable: 'yes' }));
// updateAvailable is string 'yes' which is truthy but latestVersion is undefined
const result = checkForUpdates(cachePath);
expect(result.updateAvailable).toBe(false);
});
});

describe('compareVersions', () => {
test('equal versions return 0', () => {
expect(compareVersions('260403.1', '260403.1')).toBe(0);
});

test('a > b returns positive', () => {
expect(compareVersions('260403.2', '260403.1')).toBeGreaterThan(0);
});

test('a < b returns negative', () => {
expect(compareVersions('260403.1', '260403.2')).toBeLessThan(0);
});

test('handles multi-digit segments correctly (the bug)', () => {
// String comparison: "260403.9" > "260403.10" → true (wrong)
// Numeric comparison: 9 < 10 → correct
expect(compareVersions('260403.10', '260403.9')).toBeGreaterThan(0);
expect(compareVersions('260403.9', '260403.10')).toBeLessThan(0);
});

test('handles different segment counts', () => {
expect(compareVersions('260403.1.1', '260403.1')).toBeGreaterThan(0);
expect(compareVersions('260403', '260403.0')).toBe(0);
});

test('handles major version differences', () => {
expect(compareVersions('260404.1', '260403.99')).toBeGreaterThan(0);
});
});
Loading
Loading