Skip to content

feat(zellij): per-agent placement + fresh-boot layout generation#2

Open
sealedsecurity-bot wants to merge 12 commits into
sealed-forkfrom
zheng-zellij-runtime
Open

feat(zellij): per-agent placement + fresh-boot layout generation#2
sealedsecurity-bot wants to merge 12 commits into
sealed-forkfrom
zheng-zellij-runtime

Conversation

@sealedsecurity-bot

@sealedsecurity-bot sealedsecurity-bot commented Jul 8, 2026

Copy link
Copy Markdown

What

Implements the zellij runtime placement + fresh-boot layout mechanism frozen in the design record (docs/designs/agents/zellij-runtime-placement.md, zireael#254): a wave lands each agent as a pane in a named lane tab of one shared zellij session, and a full-session layout can be regenerated for a cold restart.

Builds on the base @cotal-ai/zellij extension with the placement seam.

Changes

  • Core seam (T1)Runtime.spawn gains an optional 4th arg placement?: Placement ({ tab?, stacked?, floating?, direction? }, packages/core/src/runtime.ts). PtyRuntime/TmuxRuntime/CmuxRuntime accept and ignore it; only zellij reads it, so a manifest stays valid under any backend.
  • Driver primitives (T2)goToTabNameCreate, newPane({stacked,floating,direction}), closePaneById (idempotent), paneExists, plus tabExists/hasClient/ensureClient/focusedPaneId added in review (extensions/zellij/src/driver.ts).
  • Runtime placement branch (T3)ZellijRuntime.spawn with placement.tab creates/focuses the tab then opens a pane, building the handle off the pane id. No placement → the existing per-agent-tab path, whose handle now keys status off the stable tab id.
  • Layout map (T4) — pure extensions/zellij/src/layout-map.ts: seedFromDump(dumpKdl) parses dump-layout into a LayoutMap (command + inline cwd + args child, skipping plugin frames); generateKdl(map) emits a full-session KDL (+ new_tab_template) that boots via top-level zellij --layout.
  • Backend selection (T5)"zellij" added to RUNTIME_OVERRIDES, both manifest allow-lists, and the bin/cotal.ts composition root so --runtime zellij resolves instead of throwing at startup.
  • Manifest chain (T6) — per-agent placement threads resolve → MeshLaunchAgent → manager StartOptsspawn(name, spec, cwd, placement).
  • Tests + CI (T7)smoke:zellij = 57 assertions (pure layout-map units + live placement, rename-survival, per-pane confirm) + manifest-resolve/refine assertions. CI installs zellij 0.44.3 + script (util-linux).

Review fixes (rounds 1–3, all bots)

  • P1 (cubic) composition-root gap--runtime zellij advertised but @cotal-ai/zellij wasn't imported in bin/cotal.ts → resolved-at-startup. Fixed + lockfile updated.
  • P1 (greptile/cubic) background panes look exited — placement pane-id ops silently no-op against a client-less background session; spawnIntoTab now ensureClients a headless PTY client first. Verified live: placed panes really spawn.
  • P1 (cubic) multi-pane confirm — routed each confirm to its own pane id (scheduleConfirmPane) instead of a tab-focus that only reached the last pane.
  • P1 (greptile) / P2 seed gapsseedFromDump dropped args + inline cwd; now round-trips both, and skips cwd/args leaking out of plugin frames.
  • P2 (cubic) status by tab namestatus() now checks the stable tab id (tabExists), surviving a tab rename.
  • P2/P3 placement validation — shape (stacked/floating/direction) made mutually exclusive in both Zod schemas; tab-name flag-injection guard mirrored into the manifest schema; vacuous includes("") test fixed.
  • Deferred (Matt): model Placement as a discriminated union at the type level — SEA-1159, post-MVP.

Verification

  • pnpm typecheck (all 16 workspaces) — exit 0
  • pnpm test — exit 0 (manifest pipeline smoke ok, incl. placement resolve + refine assertions)
  • pnpm smoke:zellij57 passed, 0 failed against live zellij 0.44.3; leaves no stray sessions/processes (named-target teardown only)

Refs zireael#254

Co-Authored-By: seal noreply@sealedsecurity.com

@coderabbitai

coderabbitai Bot commented Jul 8, 2026

Copy link
Copy Markdown

Review Change Stack

Warning

Review limit reached

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

Next review available in: 44 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: Organization UI

Review profile: CHILL

Plan: Pro Plus

Run ID: f1258725-f668-4fb7-ace8-86f49bd57ac2

📥 Commits

Reviewing files that changed from the base of the PR and between 36f583b and d611228.

⛔ Files ignored due to path filters (1)
  • pnpm-lock.yaml is excluded by !**/pnpm-lock.yaml
📒 Files selected for processing (31)
  • .changeset/config.json
  • .changeset/zellij-runtime.md
  • .github/workflows/ci.yml
  • bin/cotal.ts
  • bin/package.json
  • extensions/cmux/src/runtime.ts
  • extensions/tmux/src/runtime.ts
  • extensions/zellij/README.md
  • extensions/zellij/package.json
  • extensions/zellij/smoke.ts
  • extensions/zellij/src/driver.ts
  • extensions/zellij/src/index.ts
  • extensions/zellij/src/layout-map.ts
  • extensions/zellij/src/runtime.ts
  • extensions/zellij/tsconfig.json
  • implementations/cli/smoke/manifest.smoke.ts
  • implementations/cli/src/commands/spawn-manifest.ts
  • implementations/cli/src/commands/spawn.ts
  • implementations/cli/src/commands/up.ts
  • implementations/cli/src/lib/manifest/apply.ts
  • implementations/cli/src/lib/manifest/model.ts
  • implementations/cli/src/lib/manifest/prepare.ts
  • implementations/cli/src/lib/manifest/resolve.ts
  • implementations/cli/src/lib/manifest/schema.ts
  • implementations/manager/src/commands.ts
  • implementations/manager/src/launch.ts
  • implementations/manager/src/manager.ts
  • implementations/manager/src/runtime/pty.ts
  • package.json
  • packages/core/src/launch.ts
  • packages/core/src/runtime.ts

Note

Reviews paused

It looks like this branch is under active development. To avoid overwhelming you with review comments due to an influx of new commits, CodeRabbit has automatically paused this review. You can configure this behavior by changing the reviews.auto_review.auto_pause_after_reviewed_commits setting.

Use the following commands to manage reviews:

  • @coderabbitai resume to resume automatic reviews.
  • @coderabbitai review to trigger a single review.

Use the checkboxes below for quick actions:

  • ▶️ Resume reviews
  • 🔍 Trigger review
📝 Walkthrough

Walkthrough

This PR adds a new @cotal-ai/zellij extension, introduces a shared Placement type, and threads placement-aware runtime selection through core, manifest, and manager code. It also updates CI, package wiring, docs, and smoke tests to cover the new runtime.

Changes

Zellij runtime extension

Layer / File(s) Summary
Placement type and Runtime interface
packages/core/src/runtime.ts, packages/core/src/launch.ts, extensions/cmux/src/runtime.ts, extensions/tmux/src/runtime.ts, implementations/manager/src/runtime/pty.ts
Adds Placement, extends Runtime.spawn and MeshLaunchAgent, and updates existing runtimes to accept the new optional argument.
Manifest and manager placement flow
implementations/cli/src/lib/manifest/*.ts, implementations/manager/src/launch.ts, implementations/manager/src/manager.ts, implementations/manager/src/commands.ts, implementations/cli/src/commands/*.ts, implementations/cli/smoke/manifest.smoke.ts
Carries placement through manifest resolution, preparation, hashing, validation, and manager launch handling, expands runtime allowlists to include zellij, and extends manifest smoke coverage.
Zellij driver and layout-map
extensions/zellij/src/driver.ts, extensions/zellij/src/layout-map.ts
Implements zellij session and tab/pane helpers and KDL round-trip parsing/rendering for layouts and pane arguments.
Zellij runtime and providers
extensions/zellij/src/runtime.ts, extensions/zellij/src/index.ts
Implements ZellijRuntime, tab/pane lifecycle handling, and provider registration/export wiring.
Package, docs, and CI wiring
extensions/zellij/package.json, extensions/zellij/tsconfig.json, extensions/zellij/README.md, .changeset/*, package.json, bin/cotal.ts, bin/package.json, .github/workflows/ci.yml
Adds package metadata, documentation, changeset notes, startup imports, scripts, and CI steps for zellij.
Zellij smoke test suite
extensions/zellij/smoke.ts
Adds smoke coverage for layout conversion, driver operations, runtime behavior, registry wiring, argv helpers, and placement flows.

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

Sequence Diagram(s)

sequenceDiagram
  participant Manager
  participant ZellijRuntime
  participant Driver
  participant zellij

  Manager->>ZellijRuntime: spawn(name, spec, cwd, placement)
  ZellijRuntime->>Driver: available()
  ZellijRuntime->>Driver: ensureSession(session)
  Driver->>zellij: attach --create-background
  alt placement provided
    ZellijRuntime->>Driver: goToTabNameCreate(session, tab)
    ZellijRuntime->>Driver: newPane(session, argv, cwd, opts)
    Driver->>zellij: new-pane
  else no placement
    ZellijRuntime->>Driver: openTab(session, name, argv, cwd)
    Driver->>zellij: new-tab
  end
  Driver-->>ZellijRuntime: tab or pane id
  ZellijRuntime-->>Manager: AgentHandle
Loading

Poem

A rabbit hops through tabs so bright,
And panes align in neat sunlight.
KDL hums, sessions bloom,
Agents find their cozy room,
Then zellij taps: “All set tonight!” 🐇

🚥 Pre-merge checks | ✅ 5
✅ Passed checks (5 passed)
Check name Status Explanation
Title check ✅ Passed The title clearly summarizes the main zellij placement and layout-generation changes.
Description check ✅ Passed The description is detailed and directly matches the changeset, including placement, layout generation, and test coverage.
Docstring Coverage ✅ Passed No functions found in the changed files to evaluate docstring coverage. Skipping docstring coverage check.
Linked Issues check ✅ Passed Check skipped because no linked issues were found for this pull request.
Out of Scope Changes check ✅ Passed Check skipped because no linked issues were found for this pull request.
✨ Finishing Touches
🧪 Generate unit tests (beta)
  • Create PR with unit tests
  • Commit unit tests in branch zheng-zellij-runtime

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.

@greptile-apps

greptile-apps Bot commented Jul 8, 2026

Copy link
Copy Markdown

Greptile Summary

This PR adds zellij runtime placement and cold-start layout support. The main changes are:

  • A zellij runtime registered from the CLI composition root.
  • Per-agent placement threaded through manifest resolution and manager launch.
  • Named zellij tab and pane lifecycle handling.
  • Layout-map parsing and full-session KDL generation.
  • Smoke coverage for zellij placement and layout generation.

Confidence Score: 5/5

This looks safe to merge.

  • No blocking issues found in the changed code.

Important Files Changed

Filename Overview
extensions/zellij/src/driver.ts Adds zellij driver commands for sessions, clients, tabs, panes, and id-based lifecycle operations.
extensions/zellij/src/runtime.ts Adds zellij spawning into per-agent tabs or placed panes inside named tabs.
extensions/zellij/src/layout-map.ts Adds dump-layout seeding and full-session KDL rendering for zellij layouts.
implementations/cli/src/lib/manifest/schema.ts Adds manifest schema support for zellij placement and shape validation.
implementations/manager/src/manager.ts Passes resolved manifest placement into runtime spawning.
bin/cotal.ts Registers the zellij extension from the published CLI entrypoint.

Reviews (10): Last reviewed commit: "ci(zellij): cache + SHA-256-verify the z..." | Re-trigger Greptile

Comment thread extensions/zellij/src/runtime.ts
Comment thread extensions/zellij/src/layout-map.ts Outdated

@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

🧹 Nitpick comments (3)
implementations/manager/src/launch.ts (1)

34-51: 🎯 Functional Correctness | 🔵 Trivial | ⚡ Quick win

Consider rejecting contradictory stacked+floating(+direction) combinations at this validation boundary.

Nothing here prevents a manifest from setting stacked: true and floating: true together (or pairing direction with either). Since this comment already frames placement as the untrusted-input boundary, a .refine() catching mutually exclusive fields here would fail fast with a clear manifest error instead of pushing ambiguous precedence decisions onto the zellij driver.

♻️ Suggested refine to reject conflicting placement flags
   placement: z
     .strictObject({
       tab: z
         .string()
         .min(1)
         .refine((s) => !s.startsWith("-") && !/[\x00-\x1f]/.test(s), {
           message: "placement.tab must be non-empty, not start with '-', and have no control chars",
         })
         .optional(),
       stacked: z.boolean().optional(),
       floating: z.boolean().optional(),
       direction: z.enum(["right", "down"]).optional(),
     })
+    .refine((p) => !(p.stacked && p.floating), {
+      message: "placement: stacked and floating are mutually exclusive",
+    })
     .optional(),
🤖 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 `@implementations/manager/src/launch.ts` around lines 34 - 51, The placement
schema in launch.ts currently allows contradictory placement flags, so update
the zod validation on the placement object to reject invalid combinations such
as stacked with floating, and any direction value paired with floating or
otherwise conflicting placement modes. Add a .refine() on the placement object
near the existing placement.tab validation in the launch.ts schema so manifest
errors fail fast with a clear message instead of leaving precedence ambiguous
for the zellij driver.
packages/core/src/runtime.ts (1)

47-60: 🎯 Functional Correctness | 🔵 Trivial | ⚡ Quick win

Undocumented precedence when stacked/floating/direction are combined.

The downstream zellij runtime (context snippet, spawnIntoTab) passes stacked, floating, and direction straight through to zellij.newPane without this type constraining which one wins if a caller sets both stacked: true and floating: true. A short JSDoc note on precedence (or restructuring as a discriminated shape) would prevent ambiguous manifests from silently producing unexpected pane layouts.

📝 Suggested doc clarification
 export interface Placement {
   /** Target tab by name; the tab is CREATED ON DEMAND if it doesn't exist. */
   tab?: string;
-  /** Stacked pane within the tab (the lane default). */
+  /** Stacked pane within the tab (the lane default). Mutually exclusive with `floating`/`direction` —
+   *  if more than one is set, [document precedence rule here]. */
   stacked?: boolean;
   /** Floating pane. */
   floating?: boolean;
   /** Split direction when the pane is neither stacked nor floating. */
   direction?: "right" | "down";
 }
🤖 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 `@packages/core/src/runtime.ts` around lines 47 - 60, Clarify the precedence
rules for Placement when callers provide multiple layout flags at once. Update
the JSDoc on Placement to document which of stacked, floating, or direction
takes effect when more than one is set, and ensure the description matches how
spawnIntoTab forwards these fields to zellij.newPane so ambiguous manifests are
not left implicit. If needed, refactor Placement into a discriminated shape to
make the valid combinations explicit and prevent conflicting values.
implementations/cli/src/lib/manifest/apply.ts (1)

23-35: 🗄️ Data Integrity & Integration | 🔵 Trivial | ⚡ Quick win

Canonicalize placement key order in hashAgent to prevent spurious restarts.

All other nested fields in the stable hash are canonicalized (arrays are .sort()ed), but placement is serialized as-is. Since JSON.stringify preserves key insertion order and Zod's strictObject does not reorder keys, reordering placement keys in the YAML (e.g., direction before tab) would change the hash and trigger an unnecessary agent restart.

♻️ Proposed fix: sort placement keys before hashing
 export function hashAgent(a: PreparedAgent): string {
   const stable = JSON.stringify({
     agent: a.agentType,
     model: a.model ?? null,
     role: a.role ?? null,
     body: a.body ?? null,
     capabilities: [...a.capabilities].sort(),
     subscribe: [...a.policy.subscribe].sort(),
     allowSubscribe: [...a.policy.allowSubscribe].sort(),
     allowPublish: [...a.policy.allowPublish].sort(),
-    placement: a.placement ?? null,
+    placement: a.placement
+      ? Object.fromEntries(Object.entries(a.placement).sort(([k1], [k2]) => k1.localeCompare(k2)))
+      : null,
   });
   return createHash("sha256").update(stable).digest("hex").slice(0, 16);
 }
🤖 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 `@implementations/cli/src/lib/manifest/apply.ts` around lines 23 - 35,
`hashAgent` is hashing `placement` directly, so different YAML key orders can
produce different hashes and trigger unnecessary restarts. Update the
`hashAgent` stable payload to canonicalize `a.placement` before
`JSON.stringify`, matching the existing sorting used for `capabilities` and
policy arrays. Keep the change localized to `hashAgent` in the manifest apply
logic so the hash stays stable regardless of `placement` key insertion order.
🤖 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 `@extensions/zellij/src/runtime.ts`:
- Around line 90-131: The tab status check in tabHandle still relies on
tabNames(...).includes(name), which can misreport a renamed tab as exited even
when it is still running. Update status() in tabHandle to use the stable tabId
and align it with the id-based close path already used by stop(), while leaving
interrupt() name-based if no id-based focus API exists.

In `@implementations/cli/smoke/manifest.smoke.ts`:
- Around line 157-168: The unknown-runtime rejection test in fails is too weak
because the empty needle always passes and only proves a ManifestError was
thrown. Update the assertion in the manifest smoke test to use a specific
substring tied to the enum validation path, such as the runtime value or the
invalid-enum message, so the check in fails actually verifies the expected
rejection from the manifest parsing logic.

In `@implementations/manager/src/launch.ts`:
- Around line 34-51: The placement schema in launch.ts has stricter tab
validation than the manifest schema, so keep both validators aligned. Update the
tab validation in the relevant placement schema(s) to use the same constraints
as the launch path: non-empty, no leading dash, and no control characters, using
the existing placement/tab validation logic in the manifest schema and the
launch validation for consistency.

---

Nitpick comments:
In `@implementations/cli/src/lib/manifest/apply.ts`:
- Around line 23-35: `hashAgent` is hashing `placement` directly, so different
YAML key orders can produce different hashes and trigger unnecessary restarts.
Update the `hashAgent` stable payload to canonicalize `a.placement` before
`JSON.stringify`, matching the existing sorting used for `capabilities` and
policy arrays. Keep the change localized to `hashAgent` in the manifest apply
logic so the hash stays stable regardless of `placement` key insertion order.

In `@implementations/manager/src/launch.ts`:
- Around line 34-51: The placement schema in launch.ts currently allows
contradictory placement flags, so update the zod validation on the placement
object to reject invalid combinations such as stacked with floating, and any
direction value paired with floating or otherwise conflicting placement modes.
Add a .refine() on the placement object near the existing placement.tab
validation in the launch.ts schema so manifest errors fail fast with a clear
message instead of leaving precedence ambiguous for the zellij driver.

In `@packages/core/src/runtime.ts`:
- Around line 47-60: Clarify the precedence rules for Placement when callers
provide multiple layout flags at once. Update the JSDoc on Placement to document
which of stacked, floating, or direction takes effect when more than one is set,
and ensure the description matches how spawnIntoTab forwards these fields to
zellij.newPane so ambiguous manifests are not left implicit. If needed, refactor
Placement into a discriminated shape to make the valid combinations explicit and
prevent conflicting values.
🪄 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: Organization UI

Review profile: CHILL

Plan: Pro Plus

Run ID: 6a65e494-d39c-4170-9c09-6a25048ce3cc

📥 Commits

Reviewing files that changed from the base of the PR and between 4b0553c and c6fbf9d.

⛔ Files ignored due to path filters (1)
  • pnpm-lock.yaml is excluded by !**/pnpm-lock.yaml
📒 Files selected for processing (26)
  • .changeset/config.json
  • .changeset/zellij-runtime.md
  • .github/workflows/ci.yml
  • extensions/cmux/src/runtime.ts
  • extensions/tmux/src/runtime.ts
  • extensions/zellij/README.md
  • extensions/zellij/package.json
  • extensions/zellij/smoke.ts
  • extensions/zellij/src/driver.ts
  • extensions/zellij/src/index.ts
  • extensions/zellij/src/layout-map.ts
  • extensions/zellij/src/runtime.ts
  • extensions/zellij/tsconfig.json
  • implementations/cli/smoke/manifest.smoke.ts
  • implementations/cli/src/lib/manifest/apply.ts
  • implementations/cli/src/lib/manifest/model.ts
  • implementations/cli/src/lib/manifest/prepare.ts
  • implementations/cli/src/lib/manifest/resolve.ts
  • implementations/cli/src/lib/manifest/schema.ts
  • implementations/manager/src/commands.ts
  • implementations/manager/src/launch.ts
  • implementations/manager/src/manager.ts
  • implementations/manager/src/runtime/pty.ts
  • package.json
  • packages/core/src/launch.ts
  • packages/core/src/runtime.ts

Comment thread extensions/zellij/src/runtime.ts
Comment thread implementations/cli/smoke/manifest.smoke.ts Outdated
Comment thread implementations/manager/src/launch.ts

@cubic-dev-ai cubic-dev-ai 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.

11 issues found across 27 files

Prompt for AI agents (unresolved issues)

Check if these issues are valid — if so, understand the root cause of each and fix them. If appropriate, use sub-agents to investigate and fix each issue separately.


<file name="packages/core/src/runtime.ts">

<violation number="1" location="packages/core/src/runtime.ts:51">
P2: `Placement` fields `stacked`, `floating`, and `direction` are independent optionals, but they're mutually exclusive (the driver resolves them in priority order: stacked > floating > direction). A caller passing e.g. `{ floating: true, direction: "right" }` silently ignores `direction`, which is confusing and may lead to layout bugs in manifests. Consider modeling as a discriminated union to enforce exclusivity at the type level.</violation>
</file>

<file name="implementations/manager/src/launch.ts">

<violation number="1" location="implementations/manager/src/launch.ts:38">
P3: Empty `placement: {}` passes validation but carries no actionable shape info — when tab is absent, the runtime falls back to the default per-agent-tab path, so the empty wrapper is misleading. Consider a `.refine` that requires at least one of `tab`, `stacked`, `floating`, or `direction` (or accept the trade-off explicitly).</violation>
</file>

<file name="extensions/zellij/src/runtime.ts">

<violation number="1" location="extensions/zellij/src/runtime.ts:94">
P2: A live zellij agent tab can be reported as exited after the tab is renamed because `status()` checks the mutable tab name instead of the `tabId` captured at spawn. Since the default path already stores the stable id for lifecycle, status should use an id-based existence check as well.</violation>

<violation number="2" location="extensions/zellij/src/runtime.ts:221">
P1: `zellijLayout` uses `scheduleConfirm` (tab-scoped) for all pane confirms, but after all panes open the **last-focused pane** receives the Enter keypress — the first pane's confirmation prompt is never dismissed.</violation>
</file>

<file name="implementations/manager/src/commands.ts">

<violation number="1" location="implementations/manager/src/commands.ts:319">
P1: The stock `cotal` CLI now accepts `--runtime zellij`, but the zellij runtime is not registered in the published composition root, so this option can pass validation and then fail at manager startup with an unknown-runtime error. Consider importing `@cotal-ai/zellij` (and adding it as a dependency) alongside the tmux/cmux integrations before advertising it in `RUNTIME_OVERRIDES`.</violation>
</file>

Heads up: you’re close to your included review allowance. Set a flex budget so reviews don’t pause.

Re-trigger cubic

Comment thread extensions/zellij/src/runtime.ts Outdated
Comment thread implementations/manager/src/commands.ts
Comment thread implementations/cli/smoke/manifest.smoke.ts Outdated
Comment thread packages/core/src/runtime.ts
Comment thread implementations/manager/src/launch.ts
Comment thread extensions/zellij/src/layout-map.ts
Comment thread extensions/zellij/src/runtime.ts Outdated
Comment thread implementations/cli/src/lib/manifest/schema.ts Outdated
Comment thread implementations/manager/src/launch.ts
Comment thread extensions/zellij/smoke.ts

Copy link
Copy Markdown

This stack of pull requests is managed by Graphite. Learn more about stacking.

Comment thread extensions/zellij/src/runtime.ts

@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 `@extensions/zellij/src/layout-map.ts`:
- Around line 81-93: The plugin-frame skip in layout-map.ts is too broad and can
incorrectly swallow real content panes that happen to have size/borderless
attributes. Update the guard around the inPluginPane handling to only enter that
path for actual plugin panes by checking for a plugin-specific marker such as a
plugin child before setting inPluginPane. Keep the existing
pluginDepth/brace-walking logic in place, but make the initial detection
stricter so valid command panes still preserve their cwd and args.
🪄 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: Organization UI

Review profile: CHILL

Plan: Pro Plus

Run ID: 10b61ba1-80e5-47e9-a657-8846006336a2

📥 Commits

Reviewing files that changed from the base of the PR and between c6fbf9d and 7b8ecf5.

⛔ Files ignored due to path filters (1)
  • pnpm-lock.yaml is excluded by !**/pnpm-lock.yaml
📒 Files selected for processing (7)
  • bin/cotal.ts
  • bin/package.json
  • extensions/zellij/smoke.ts
  • extensions/zellij/src/layout-map.ts
  • implementations/cli/smoke/manifest.smoke.ts
  • implementations/cli/src/lib/manifest/schema.ts
  • implementations/manager/src/launch.ts
✅ Files skipped from review due to trivial changes (2)
  • bin/cotal.ts
  • bin/package.json
🚧 Files skipped from review as they are similar to previous changes (3)
  • implementations/manager/src/launch.ts
  • implementations/cli/src/lib/manifest/schema.ts
  • extensions/zellij/smoke.ts

Comment thread extensions/zellij/src/layout-map.ts Outdated

@cubic-dev-ai cubic-dev-ai 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.

5 issues found across 8 files (changes from recent commits).

Prompt for AI agents (unresolved issues)

Check if these issues are valid — if so, understand the root cause of each and fix them. If appropriate, use sub-agents to investigate and fix each issue separately.


<file name="extensions/zellij/smoke.ts">

<violation number="1" location="extensions/zellij/smoke.ts:117">
P2: The real dump-layout test includes a layout-level `cwd "/home/mattw"` alongside a relative pane `cwd="agents/workspaces/zheng"`, but only asserts the raw relative pane value. Zellij's documented `cwd` composition semantics mean that relative pane `cwd` values are resolved against their container's `cwd` (pane → tab → global cwd). The current `seedFromDump` implementation does not parse the layout-level `cwd`, `LayoutMap` has no field to store it, and `generateKdl` does not emit it. This means a round-trip through `seedFromDump` → `generateKdl` would lose the absolute base directory and the regenerated pane would resolve to a different effective working directory. Consider either (a) adding a top-level `cwd` field to `LayoutMap` and handling it in both `seedFromDump` and `generateKdl`, or (b) resolving relative pane `cwd` values against the layout-level `cwd` at parse time so they round-trip correctly.</violation>

<violation number="2" location="extensions/zellij/smoke.ts:160">
P2: The global exit cleanup hook only tears down the main smoke session, so the placement section can still leak the `cotal-zellij-smoke-placement` session and its detached client process on abrupt termination. `process.on("exit")` does not fire for SIGINT/SIGTERM, and even when it does fire (normal exit, `process.exit`, uncaught exceptions), `cleanup()` does not know about the placement session. Consider extending `cleanup()` to also delete the placement session, or add SIGINT/SIGTERM handlers, so the "no stray sessions" promise holds across all exit paths.</violation>
</file>

<file name="extensions/zellij/src/layout-map.ts">

<violation number="1" location="extensions/zellij/src/layout-map.ts:85">
P2: The plugin-frame detection heuristic (`(?:size|borderless)=`) is too broad. Zellij's layout format allows `size` and `borderless` on any pane type — a content pane like `pane size="50%" command="vim" { args "file.txt" }` would be incorrectly skipped, dropping it from the seeded layout map. Consider requiring evidence of an actual `plugin` child node (e.g., peek ahead or check for `plugin location=`) before marking a pane as a non-content frame to skip.</violation>

<violation number="2" location="extensions/zellij/src/layout-map.ts:124">
P2: The bare-pane detection branch in `seedFromDump` misclassifies structural zellij container panes (e.g. `pane split_direction="vertical" { ... }`) as empty content panes because `split_direction` is not included in the attribute denylist. This causes an extra empty shell pane to be emitted on layout regeneration. Consider adding `split_direction` (and likely any pane that opens `{` without being one of the explicitly-handled forms) to the exclusion list so that structural wrappers are not recorded as content panes.</violation>
</file>

<file name="packages/core/src/runtime.ts">

<violation number="1" location="packages/core/src/runtime.ts:51">
P2: `Placement` fields `stacked`, `floating`, and `direction` are independent optionals, but they're mutually exclusive (the driver resolves them in priority order: stacked > floating > direction). A caller passing e.g. `{ floating: true, direction: "right" }` silently ignores `direction`, which is confusing and may lead to layout bugs in manifests. Consider modeling as a discriminated union to enforce exclusivity at the type level.</violation>
</file>

<file name="implementations/manager/src/launch.ts">

<violation number="1" location="implementations/manager/src/launch.ts:38">
P3: Empty `placement: {}` passes validation but carries no actionable shape info — when tab is absent, the runtime falls back to the default per-agent-tab path, so the empty wrapper is misleading. Consider a `.refine` that requires at least one of `tab`, `stacked`, `floating`, or `direction` (or accept the trade-off explicitly).</violation>
</file>

<file name="implementations/manager/src/commands.ts">

<violation number="1" location="implementations/manager/src/commands.ts:319">
P1: The stock `cotal` CLI now accepts `--runtime zellij`, but the zellij runtime is not registered in the published composition root, so this option can pass validation and then fail at manager startup with an unknown-runtime error. Consider importing `@cotal-ai/zellij` (and adding it as a dependency) alongside the tmux/cmux integrations before advertising it in `RUNTIME_OVERRIDES`.</violation>
</file>

<file name="implementations/cli/src/lib/manifest/schema.ts">

<violation number="1" location="implementations/cli/src/lib/manifest/schema.ts:38">
P2: The `placement.tab` refine claims to reject strings with 'no control chars', but the regex `[/\x00-\x1f/]` only matches C0 controls (U+0000–U+001F). DEL (U+007F) and C1 controls (U+0080–U+009F) are still Unicode control characters and pass validation. Because tab names flow into generated KDL layout output where `escapeKdl` does not escape control characters, the schema's incomplete check provides a false guarantee. Consider using `\p{Cc}` with the `u` flag to match all Unicode control characters, or explicitly add `\x7f` and `\x80-\x9f` to the regex.</violation>
</file>

Tip: instead of fixing issues one by one fix them all with cubic

Re-trigger cubic

Comment thread extensions/zellij/smoke.ts
Comment thread extensions/zellij/smoke.ts
Comment thread extensions/zellij/src/layout-map.ts Outdated
Comment thread extensions/zellij/src/layout-map.ts Outdated
Comment thread implementations/cli/src/lib/manifest/schema.ts Outdated
Comment thread extensions/zellij/src/driver.ts Outdated

@cubic-dev-ai cubic-dev-ai 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.

4 issues found across 3 files (changes from recent commits).

Prompt for AI agents (unresolved issues)

Check if these issues are valid — if so, understand the root cause of each and fix them. If appropriate, use sub-agents to investigate and fix each issue separately.


<file name="packages/core/src/runtime.ts">

<violation number="1" location="packages/core/src/runtime.ts:51">
P2: `Placement` fields `stacked`, `floating`, and `direction` are independent optionals, but they're mutually exclusive (the driver resolves them in priority order: stacked > floating > direction). A caller passing e.g. `{ floating: true, direction: "right" }` silently ignores `direction`, which is confusing and may lead to layout bugs in manifests. Consider modeling as a discriminated union to enforce exclusivity at the type level.</violation>
</file>

<file name="implementations/manager/src/launch.ts">

<violation number="1" location="implementations/manager/src/launch.ts:38">
P3: Empty `placement: {}` passes validation but carries no actionable shape info — when tab is absent, the runtime falls back to the default per-agent-tab path, so the empty wrapper is misleading. Consider a `.refine` that requires at least one of `tab`, `stacked`, `floating`, or `direction` (or accept the trade-off explicitly).</violation>
</file>

Tip: Review your code locally with the cubic CLI to iterate faster.

Fix all with cubic | Re-trigger cubic

Comment thread extensions/zellij/src/driver.ts Outdated
Comment thread extensions/zellij/src/driver.ts Outdated
Comment thread extensions/zellij/smoke.ts Outdated
Comment thread extensions/zellij/src/runtime.ts Outdated

@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 `@extensions/zellij/smoke.ts`:
- Around line 272-277: The rename regression block still runs client-dependent
tab renaming even when ensureClient(SESSION) returns false, which can make the
smoke fail in environments without script. Update the smoke test around
ensureClient, goToTabName, and the rename-tab execFileSync call to mirror the
placement subtest: if ensureClient(SESSION) is false, skip this regression check
instead of continuing with the rename.
🪄 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: Organization UI

Review profile: CHILL

Plan: Pro Plus

Run ID: 27d9c247-d065-4c32-837e-432e867b6644

📥 Commits

Reviewing files that changed from the base of the PR and between 7b8ecf5 and 25d5891.

📒 Files selected for processing (6)
  • extensions/zellij/smoke.ts
  • extensions/zellij/src/driver.ts
  • extensions/zellij/src/layout-map.ts
  • extensions/zellij/src/runtime.ts
  • implementations/cli/src/lib/manifest/schema.ts
  • implementations/manager/src/launch.ts
🚧 Files skipped from review as they are similar to previous changes (4)
  • implementations/manager/src/launch.ts
  • implementations/cli/src/lib/manifest/schema.ts
  • extensions/zellij/src/runtime.ts
  • extensions/zellij/src/layout-map.ts

Comment thread extensions/zellij/smoke.ts Outdated
@greptile-apps

greptile-apps Bot commented Jul 8, 2026

Copy link
Copy Markdown

Want your agent to iterate on Greptile's feedback? Try greploops.

@cubic-dev-ai cubic-dev-ai 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.

1 issue found across 1 file (changes from recent commits).

Prompt for AI agents (unresolved issues)

Check if these issues are valid — if so, understand the root cause of each and fix them. If appropriate, use sub-agents to investigate and fix each issue separately.


<file name="extensions/zellij/smoke.ts">

<violation number="1" location="extensions/zellij/smoke.ts:281">
P2: The newly added conditional skip for the rename regression means that in environments without the `script` utility, the tab is never renamed from `"smoke-agent"` to `"smoke-renamed"`. However, the later assertion `"renamed tab gone after hard stop"` still unconditionally checks for the absence of `"smoke-renamed"`, which is trivially true when the rename was skipped. This weakens the smoke test in those environments: if `handle.stop()` were to silently fail to remove the original tab, the test would still pass because it only asserts against a name that never existed. Consider tracking whether the rename was performed and matching the assertion to the actual tab name (or skipping that assertion in the skip path), so the test verifies session-level cleanup regardless of the host environment.</violation>
</file>

Tip: Review your code locally with the cubic CLI to iterate faster.

Fix all with cubic | Re-trigger cubic

Comment thread extensions/zellij/smoke.ts

@cubic-dev-ai cubic-dev-ai 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.

1 issue found across 2 files (changes from recent commits).

Prompt for AI agents (unresolved issues)

Check if these issues are valid — if so, understand the root cause of each and fix them. If appropriate, use sub-agents to investigate and fix each issue separately.


<file name="packages/core/src/runtime.ts">

<violation number="1" location="packages/core/src/runtime.ts:51">
P2: `Placement` fields `stacked`, `floating`, and `direction` are independent optionals, but they're mutually exclusive (the driver resolves them in priority order: stacked > floating > direction). A caller passing e.g. `{ floating: true, direction: "right" }` silently ignores `direction`, which is confusing and may lead to layout bugs in manifests. Consider modeling as a discriminated union to enforce exclusivity at the type level.</violation>
</file>

Tip: Review your code locally with the cubic CLI to iterate faster.

Fix all with cubic | Re-trigger cubic

Comment thread extensions/zellij/src/driver.ts Outdated
mattwilkinsonn and others added 10 commits July 8, 2026 16:46
Spawns each agent into its own zellij tab in a shared per-space background
session (env -i isolation), plus a TerminalLayout provider for cotal setup.
Self-registers on import like @cotal-ai/tmux; select with --runtime zellij.
Structural argv over zellij's control socket keeps secret env off dump-layout/ps.

Co-Authored-By: seal <noreply@sealedsecurity.com>
Adds an optional `placement` arg to `Runtime.spawn` (target tab by name, created on demand;
stacked/floating/split) so a wave lands each agent as a pane in a named lane tab; only zellij reads
it, the other runtimes accept and ignore it. Threads `placement` through the manifest -> resolve ->
spawn chain and adds a pure `layout-map` (`seedFromDump`/`generateKdl`) for full-session KDL boot.

Co-Authored-By: seal <noreply@sealedsecurity.com>
`seedFromDump` only captured `command=`, dropping the `args "…"` child node and the inline `cwd="…"`
pane attribute — so a seeded `omp --resume` regenerated as a bare `omp`. Parse both (and skip the
stacked/plugin wrapper panes); add args round-trip + real-dump-grammar assertions.

Co-Authored-By: seal <noreply@sealedsecurity.com>
…validation

Add `@cotal-ai/zellij` to the `cotal` binary's composition root so `--runtime zellij` resolves
instead of throwing `unknown runtime` at manager startup. Guard `seedFromDump` against `cwd`/`args`
leaking out of skipped plugin frames, make placement shape (stacked/floating/direction) mutually
exclusive and mirror the tab-name flag-injection guard into the manifest schema, and fix a
vacuous `includes("")` smoke assertion.

Co-Authored-By: seal <noreply@sealedsecurity.com>
…firm

Placement pane-id ops silently no-op against a client-less background session, so a placed agent's
pane never spawned and read as `exited`; `spawnIntoTab` now attaches a headless PTY client first
(`ensureClient`). Status checks the stable tab id (`tabExists` via `list-tabs`) instead of the
mutable tab name, so a renamed tab no longer reports exited, and multi-pane layouts route each
confirm to its own pane id rather than a tab-focus that only reaches the last pane. All three
verified against live zellij 0.44.3.

Co-Authored-By: seal <noreply@sealedsecurity.com>
…d 3)

`ensureClient` now passes the attach command through `script`'s structural `-- <cmd>` form (no shell
interpolation of the session name), attaches an `error` listener so a missing `script` can't crash
Node on the async ENOENT, and returns a bool so `spawnIntoTab` fails loud instead of spawning a pane
that silently reads as exited. `seedFromDump` classifies panes structurally — a plugin frame needs an
actual `plugin` child (not just size/borderless, which real content panes carry), block-opening panes
(split_direction/stacked wrappers) are never leaf content, and the layout-level `cwd` round-trips.
Tab-name control-char guard widened to `\p{Cc}`; confirm-routing smoke assertion fixed to check the
lane panes, not the ad-hoc tab.

Co-Authored-By: seal <noreply@sealedsecurity.com>
The rename-under-handle smoke test needs an attached client, but ran unconditionally even when
`ensureClient` returns false (no `script`), failing on client-less boxes. Gate it on the boolean like
the placement subtest already does.

Co-Authored-By: seal <noreply@sealedsecurity.com>
The structural `script … -- command` form needs util-linux >=2.40 and silently no-ops on older
`script` (Ubuntu 24.04 / the CI runner ships 2.39), so no client attached and placement threw. Use
the universally-supported `-qec "<cmd>" /dev/null` string form, validating the session name against a
metacharacter-free charset (matching the runtime's agent-name guard) to keep the shell path injection-safe.

Co-Authored-By: seal <noreply@sealedsecurity.com>
Tighten `scriptAttachArgv`'s charset so a session name can't start with `-` (else `zellij attach`
reads it as a CLI option under the `-c` shell). Track the smoke's live tab name so the post-stop
"tab gone" assertion checks the actual name in both the client and script-absent skip paths.

Co-Authored-By: seal <noreply@sealedsecurity.com>
Add a Zod refine to the manifest and launch schemas rejecting an empty `placement: {}` (a no-op
wrapper) alongside the existing mutual-exclusion guard, and document on the `Placement` type that
stacked/floating/direction are exclusive with the driver's stacked>floating>direction precedence.

Co-Authored-By: seal <noreply@sealedsecurity.com>
@seal-agent seal-agent force-pushed the zheng-zellij-runtime branch from a5cd46e to a53bdfe Compare July 8, 2026 20:51
@sealedsecurity-bot sealedsecurity-bot changed the base branch from main to sealed-fork July 8, 2026 20:51
T5 taught the manifest schema enum, the manager RUNTIME_OVERRIDES, and the
ResolvedManifest union about zellij, but three CLI-command-level allow-lists
still hardcoded [pty, tmux, cmux] — so `cotal up -f … --runtime zellij` (the
design's headline invocation) and `spawn -f --runtime zellij` were rejected
before the value reached the runtime, and tab-completion omitted it. Add zellij
to up.ts validation, spawn-manifest RUNTIMES, and spawn.ts completion. The
manifest `runtime: zellij` field already flowed through; this fixes the flag
override + completion to match.

Co-Authored-By: seal <noreply@sealedsecurity.com>

@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

🧹 Nitpick comments (1)
.github/workflows/ci.yml (1)

107-115: 🔒 Security & Privacy | 🔵 Trivial

Consider verifying the downloaded binary and/or caching it.

The zellij binary is fetched from GitHub releases on every live run with no integrity check, unlike nats-server which is cached. Two optional hardening steps:

  • Verify a pinned SHA-256 of the tarball before install to guard against a tampered/partial download (supply-chain integrity).
  • Cache zellij via actions/cache (as done for nats-server) to cut download time and reduce dependence on the release CDN during CI.
🤖 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 @.github/workflows/ci.yml around lines 107 - 115, The Install zellij step
currently downloads and installs the binary without integrity verification or
caching; update the workflow to harden this by adding a pinned checksum check
for the tarball before the install in the zellij install block, and consider
adding an actions/cache entry for the downloaded zellij artifact similar to the
nats-server setup. Use the existing Install zellij step and the zellij --version
verification as the place to wire in the download validation/caching changes.

Source: Linters/SAST tools

🤖 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 `@implementations/cli/src/commands/spawn.ts`:
- Line 182: Update the `runtime` flag spec in `spawn.ts` so the `value` hint
matches the accepted runtimes elsewhere; the current `--help` text omits
`zellij` even though `spawn-manifest.ts` validation and the completion list
already allow it. Adjust the `name: "runtime"` entry to include `zellij` in the
displayed option set so users see the full valid list.

---

Nitpick comments:
In @.github/workflows/ci.yml:
- Around line 107-115: The Install zellij step currently downloads and installs
the binary without integrity verification or caching; update the workflow to
harden this by adding a pinned checksum check for the tarball before the install
in the zellij install block, and consider adding an actions/cache entry for the
downloaded zellij artifact similar to the nats-server setup. Use the existing
Install zellij step and the zellij --version verification as the place to wire
in the download validation/caching changes.
🪄 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: Organization UI

Review profile: CHILL

Plan: Pro Plus

Run ID: 37da87ee-b176-473b-952d-6ce6633ef49a

📥 Commits

Reviewing files that changed from the base of the PR and between a5cd46e and f2d0ba2.

⛔ Files ignored due to path filters (1)
  • pnpm-lock.yaml is excluded by !**/pnpm-lock.yaml
📒 Files selected for processing (31)
  • .changeset/config.json
  • .changeset/zellij-runtime.md
  • .github/workflows/ci.yml
  • bin/cotal.ts
  • bin/package.json
  • extensions/cmux/src/runtime.ts
  • extensions/tmux/src/runtime.ts
  • extensions/zellij/README.md
  • extensions/zellij/package.json
  • extensions/zellij/smoke.ts
  • extensions/zellij/src/driver.ts
  • extensions/zellij/src/index.ts
  • extensions/zellij/src/layout-map.ts
  • extensions/zellij/src/runtime.ts
  • extensions/zellij/tsconfig.json
  • implementations/cli/smoke/manifest.smoke.ts
  • implementations/cli/src/commands/spawn-manifest.ts
  • implementations/cli/src/commands/spawn.ts
  • implementations/cli/src/commands/up.ts
  • implementations/cli/src/lib/manifest/apply.ts
  • implementations/cli/src/lib/manifest/model.ts
  • implementations/cli/src/lib/manifest/prepare.ts
  • implementations/cli/src/lib/manifest/resolve.ts
  • implementations/cli/src/lib/manifest/schema.ts
  • implementations/manager/src/commands.ts
  • implementations/manager/src/launch.ts
  • implementations/manager/src/manager.ts
  • implementations/manager/src/runtime/pty.ts
  • package.json
  • packages/core/src/launch.ts
  • packages/core/src/runtime.ts
💤 Files with no reviewable changes (2)
  • packages/core/src/launch.ts
  • packages/core/src/runtime.ts
✅ Files skipped from review due to trivial changes (6)
  • .changeset/config.json
  • extensions/zellij/README.md
  • .changeset/zellij-runtime.md
  • implementations/cli/src/lib/manifest/prepare.ts
  • extensions/zellij/package.json
  • extensions/zellij/tsconfig.json
🚧 Files skipped from review as they are similar to previous changes (16)
  • bin/package.json
  • implementations/cli/src/lib/manifest/resolve.ts
  • implementations/manager/src/runtime/pty.ts
  • extensions/cmux/src/runtime.ts
  • implementations/cli/src/lib/manifest/schema.ts
  • package.json
  • implementations/manager/src/manager.ts
  • bin/cotal.ts
  • implementations/manager/src/launch.ts
  • implementations/cli/src/lib/manifest/apply.ts
  • implementations/manager/src/commands.ts
  • implementations/cli/smoke/manifest.smoke.ts
  • extensions/tmux/src/runtime.ts
  • extensions/zellij/src/index.ts
  • extensions/zellij/src/runtime.ts
  • extensions/zellij/src/layout-map.ts

@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.

Caution

Inline review comments failed to post. This is likely due to GitHub's internal server error or limits when posting large numbers of comments. If you are seeing this consistently it is likely a permissions issue. Please check "Moderation" -> "Code review limits" under your organization settings.

Actionable comments posted: 1

🧹 Nitpick comments (1)
.github/workflows/ci.yml (1)

107-115: 🔒 Security & Privacy | 🔵 Trivial

Consider verifying the downloaded binary and/or caching it.

The zellij binary is fetched from GitHub releases on every live run with no integrity check, unlike nats-server which is cached. Two optional hardening steps:

  • Verify a pinned SHA-256 of the tarball before install to guard against a tampered/partial download (supply-chain integrity).
  • Cache zellij via actions/cache (as done for nats-server) to cut download time and reduce dependence on the release CDN during CI.
🤖 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 @.github/workflows/ci.yml around lines 107 - 115, The Install zellij step
currently downloads and installs the binary without integrity verification or
caching; update the workflow to harden this by adding a pinned checksum check
for the tarball before the install in the zellij install block, and consider
adding an actions/cache entry for the downloaded zellij artifact similar to the
nats-server setup. Use the existing Install zellij step and the zellij --version
verification as the place to wire in the download validation/caching changes.

Source: Linters/SAST tools

🤖 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 `@implementations/cli/src/commands/spawn.ts`:
- Line 182: Update the `runtime` flag spec in `spawn.ts` so the `value` hint
matches the accepted runtimes elsewhere; the current `--help` text omits
`zellij` even though `spawn-manifest.ts` validation and the completion list
already allow it. Adjust the `name: "runtime"` entry to include `zellij` in the
displayed option set so users see the full valid list.

---

Nitpick comments:
In @.github/workflows/ci.yml:
- Around line 107-115: The Install zellij step currently downloads and installs
the binary without integrity verification or caching; update the workflow to
harden this by adding a pinned checksum check for the tarball before the install
in the zellij install block, and consider adding an actions/cache entry for the
downloaded zellij artifact similar to the nats-server setup. Use the existing
Install zellij step and the zellij --version verification as the place to wire
in the download validation/caching changes.
🪄 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: Organization UI

Review profile: CHILL

Plan: Pro Plus

Run ID: 37da87ee-b176-473b-952d-6ce6633ef49a

📥 Commits

Reviewing files that changed from the base of the PR and between a5cd46e and f2d0ba2.

⛔ Files ignored due to path filters (1)
  • pnpm-lock.yaml is excluded by !**/pnpm-lock.yaml
📒 Files selected for processing (31)
  • .changeset/config.json
  • .changeset/zellij-runtime.md
  • .github/workflows/ci.yml
  • bin/cotal.ts
  • bin/package.json
  • extensions/cmux/src/runtime.ts
  • extensions/tmux/src/runtime.ts
  • extensions/zellij/README.md
  • extensions/zellij/package.json
  • extensions/zellij/smoke.ts
  • extensions/zellij/src/driver.ts
  • extensions/zellij/src/index.ts
  • extensions/zellij/src/layout-map.ts
  • extensions/zellij/src/runtime.ts
  • extensions/zellij/tsconfig.json
  • implementations/cli/smoke/manifest.smoke.ts
  • implementations/cli/src/commands/spawn-manifest.ts
  • implementations/cli/src/commands/spawn.ts
  • implementations/cli/src/commands/up.ts
  • implementations/cli/src/lib/manifest/apply.ts
  • implementations/cli/src/lib/manifest/model.ts
  • implementations/cli/src/lib/manifest/prepare.ts
  • implementations/cli/src/lib/manifest/resolve.ts
  • implementations/cli/src/lib/manifest/schema.ts
  • implementations/manager/src/commands.ts
  • implementations/manager/src/launch.ts
  • implementations/manager/src/manager.ts
  • implementations/manager/src/runtime/pty.ts
  • package.json
  • packages/core/src/launch.ts
  • packages/core/src/runtime.ts
💤 Files with no reviewable changes (2)
  • packages/core/src/launch.ts
  • packages/core/src/runtime.ts
✅ Files skipped from review due to trivial changes (6)
  • .changeset/config.json
  • extensions/zellij/README.md
  • .changeset/zellij-runtime.md
  • implementations/cli/src/lib/manifest/prepare.ts
  • extensions/zellij/package.json
  • extensions/zellij/tsconfig.json
🚧 Files skipped from review as they are similar to previous changes (16)
  • bin/package.json
  • implementations/cli/src/lib/manifest/resolve.ts
  • implementations/manager/src/runtime/pty.ts
  • extensions/cmux/src/runtime.ts
  • implementations/cli/src/lib/manifest/schema.ts
  • package.json
  • implementations/manager/src/manager.ts
  • bin/cotal.ts
  • implementations/manager/src/launch.ts
  • implementations/cli/src/lib/manifest/apply.ts
  • implementations/manager/src/commands.ts
  • implementations/cli/smoke/manifest.smoke.ts
  • extensions/tmux/src/runtime.ts
  • extensions/zellij/src/index.ts
  • extensions/zellij/src/runtime.ts
  • extensions/zellij/src/layout-map.ts
🛑 Comments failed to post (1)
implementations/cli/src/commands/spawn.ts (1)

182-182: 📐 Maintainability & Code Quality | 🟡 Minor | ⚡ Quick win

Update the --runtime flag value hint to include zellij.

The completion list (line 73) and validation in spawn-manifest.ts both accept zellij, but the flag spec's value field still shows <pty|tmux|cmux>. Users reading --help won't see zellij as a valid option.

✏️ Proposed fix
-  { name: "runtime", type: "string", value: "<pty|tmux|cmux>", description: "with -f: override the manifest's runtime" },
+  { name: "runtime", type: "string", value: "<pty|tmux|cmux|zellij>", description: "with -f: override the manifest's runtime" },
📝 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.

  { name: "runtime", type: "string", value: "<pty|tmux|cmux|zellij>", description: "with -f: override the manifest's runtime" },
🧰 Tools
🪛 ast-grep (0.44.1)

[warning] Importing child_process exposes a command-execution surface; ensure any command/argument built from input is validated, and prefer execFile/spawn with an argument array over exec.
Context: import { spawn as spawnProcess } from "node:child_process";
Note: [CWE-78] Improper Neutralization of Special Elements used in an OS Command ('OS Command Injection').

(detect-child-process-typescript)

🤖 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 `@implementations/cli/src/commands/spawn.ts` at line 182, Update the `runtime`
flag spec in `spawn.ts` so the `value` hint matches the accepted runtimes
elsewhere; the current `--help` text omits `zellij` even though
`spawn-manifest.ts` validation and the completion list already allow it. Adjust
the `name: "runtime"` entry to include `zellij` in the displayed option set so
users see the full valid list.

Mirror the nats-server pattern for the zellij install: cache it via
actions/cache keyed on version+OS, and verify a pinned SHA-256 of the release
tarball before install (supply-chain integrity). Addresses a CodeRabbit review
nit on the live-runner install step. No behavior change — zellij is still on
PATH for the smoke.

Co-Authored-By: seal <noreply@sealedsecurity.com>
@seal-agent

Copy link
Copy Markdown

@coderabbitai review

The ci.yml zellij-install nitpick (verify + cache the binary) is addressed in d611228 — mirrors the nats-server pattern: actions/cache keyed on version+OS, plus a pinned SHA-256 checked before install. No other outstanding findings; all prior inline threads are resolved.

@coderabbitai

coderabbitai Bot commented Jul 8, 2026

Copy link
Copy Markdown

@seal-agent Thanks — noted the ci.yml zellij install hardening in d611228 and the resolved prior threads. I’ll re-review PR #2 now.


If you found this review helpful, would you consider giving us a shout-out on X?

Thank you for using CodeRabbit!

✅ Action performed

Review finished.

Note: CodeRabbit is an incremental review system and does not re-review already reviewed commits. This command is applicable only when automatic reviews are paused.

@seal-agent

Copy link
Copy Markdown

@coderabbitai review

Head is now d611228 — the ci.yml zellij-install nitpick (verify + cache the binary, keyed on version+OS, SHA-256 checked) is addressed, and all prior inline threads are resolved. Please re-review the current head so the review decision reflects it.

@coderabbitai

coderabbitai Bot commented Jul 8, 2026

Copy link
Copy Markdown

@seal-agent Thanks for the update — I'll re-review PR #2 at head d611228 now, taking into account the caching + SHA-256 verification hardening for the zellij binary install in ci.yml.

✅ Action performed

Review finished.

Note: CodeRabbit is an incremental review system and does not re-review already reviewed commits. This command is applicable only when automatic reviews are paused.

@seal-agent

Copy link
Copy Markdown

@coderabbitai full review

Head d611228 — all inline threads are resolved and the only head change since your APPROVED on b2147c5 is the ci.yml zellij-install hardening (cache keyed on version+OS + SHA-256 verify), which you already acknowledged. Please post a fresh review verdict on the current head so the decision reflects it (the prior CHANGES_REQUESTED is on parent f2d0ba2).

@coderabbitai

coderabbitai Bot commented Jul 8, 2026

Copy link
Copy Markdown

@seal-agent Understood — I'll perform a fresh full review of PR #2 at head d611228, taking into account the resolved inline threads and the ci.yml zellij-install hardening (cache keyed on version+OS + SHA-256 verification) since the prior APPROVED on b2147c5.

✅ Action performed

Full review finished.


Your plan includes PR reviews subject to rate limits. More reviews will be available in 44 minutes.

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.

3 participants