Skip to content

fix(grunt/tui): hivemind sidebar — text wrapping (no more right-edge truncation)#18

Merged
terrxo merged 1 commit into
devfrom
grunt-fix/hivemind-sidebar-text-wrapping
May 27, 2026
Merged

fix(grunt/tui): hivemind sidebar — text wrapping (no more right-edge truncation)#18
terrxo merged 1 commit into
devfrom
grunt-fix/hivemind-sidebar-text-wrapping

Conversation

@terrxo

@terrxo terrxo commented May 27, 2026

Copy link
Copy Markdown

Refs hivemind anomalyco#229. Nik caught visible truncation in screenshot — peer rows showed 'nik-t...' instead of 'nik-tab-2', summaries clipped at card edge.

Root cause

Sidebar interior is ~38 cols. Opentui's <text> clips horizontally — no auto-wrap. Previous impl crammed peer-id + summary on one inline row that overflowed.

Fix

Two explicit lines per peer / inbox row. Line 1 = compact identifier + time. Line 2 = wrapped summary (wrapMode='word') truncated to safe char budget.

Local install: 1.15.10-grunt.7+local.0d4a394a8.dirty. bun typecheck clean.

… inbox (no more right-edge truncation)

Refs hivemind anomalyco#229. Nik caught visible truncation in screenshot — peer rows showing 'nik-t...' instead of 'nik-tab-2', summary lines clipped at card edge, inbox subject/body chopped to 'ack:' / 'CLAIM' fragments.

## Root cause

Sidebar interior width is ~38 cols (Sidebar is 42 wide, paddingLeft=2 + paddingRight=2). Opentui's <text> renderable clips horizontally by default — no auto-wrap on inline rows. Previous implementation crammed peer-id + summary on a single inline row which overflowed and got clipped at the right edge.

## Fix

Each peer / inbox row is now TWO explicit lines:

- **Peers**: line 1 = '· <peer-id> <relative-time>'. Line 2 = summary (wrapMode='word', truncated to 90 chars).
- **Inbox**: line 1 = '<sender>'. Line 2 = subject-or-body (wrapMode='word', truncated to 110 chars).
- **You section**: summary line gets wrapMode='word'; peer-id truncated to (SIDEBAR_INNER_COLS - 6) on overflow.

Plus a small truncate(s, max) helper that adds an ellipsis (…) so the eye sees that content was cut, not just disappeared.

Constants: SIDEBAR_INNER_COLS=38. Reflects the sidebar.tsx width=42 minus paddingLeft=2 + paddingRight=2.

## Verified

bun typecheck clean. Local install reports 1.15.10-grunt.7+local.0d4a394a8.dirty.
@terrxo terrxo merged commit c144a1d into dev May 27, 2026
3 of 10 checks passed
@terrxo terrxo deleted the grunt-fix/hivemind-sidebar-text-wrapping branch May 27, 2026 01:34
@github-actions

Copy link
Copy Markdown

Hey! Your PR title fix(grunt/tui): hivemind sidebar — text wrapping (no more right-edge truncation) doesn't follow conventional commit format.

Please update it to start with one of:

  • feat: or feat(scope): new feature
  • fix: or fix(scope): bug fix
  • docs: or docs(scope): documentation changes
  • chore: or chore(scope): maintenance tasks
  • refactor: or refactor(scope): code refactoring
  • test: or test(scope): adding or updating tests

Where scope is the package name (e.g., app, desktop, opencode).

See CONTRIBUTING.md for details.

@github-actions

Copy link
Copy Markdown

This PR doesn't fully meet our contributing guidelines and PR template.

What needs to be fixed:

  • PR description is missing required template sections. Please use the PR template.

Please edit this PR description to address the above within 2 hours, or it will be automatically closed.

If you believe this was flagged incorrectly, please let a maintainer know.

terrxo added a commit that referenced this pull request May 27, 2026
…alyco#266 Phase 1) (#21)

Bridges gruntcode's per-turn lifecycle to the hivemind-mcp loop primitive
shipped in hivemind-mcp v0.8.2 (PR #18) + extended in v0.8.3 (PR #20
await-review). Every step-finish fires hivemind_record_turn_end into the
MCP; every successful tool-result fires hivemind_loop_progress. The MCP
decides whether to auto-wake the same peer (continue), wake the parent
(escalate / await-review), or no-op. Workers self-drive toward goals
WITHOUT Nik typing.

This is Phase 1 of the loop primitive program (anomalyco#266). Phase 0 (schema +
tools + decision logic) lives in hivemind-mcp; this PR wires the gruntcode
side that emits the events.

Implementation:

- packages/opencode/src/session/hivemind-loop-hook.ts (new):
  - HivemindLoopHook.recordTurnEnd(...) — called from step-finish handler
  - HivemindLoopHook.loopProgress(...) — called from tool-result handler
  - Both take MCP.Service as Option (Effect.serviceOption) so the
    processor degrades cleanly when MCP isn't in context (tests don't
    have to provide MCP just to satisfy a single hook env requirement)
  - Both fire-and-forget via Effect.forkIn(scope) + Effect.ignore — the
    hook MUST NEVER block or throw into the TUI (loop primitive correctness
    rule; the loop is supposed to PREVENT failures, not cause them)
  - findHivemindClient: fast-path matches client named 'hivemind'; slow-
    path scans tools() map for the tool-name suffix (handles users who
    renamed their MCP under a non-conventional key)
  - buildTurnSummary deferred until AFTER client resolution to skip the
    Database.use read when we're no-opping anyway

- packages/opencode/src/effect/runtime-flags.ts:
  - New hivemindLoopEnabled flag (OPENCODE_HIVEMIND_LOOP_ENABLED env var,
    default false). Opt-in per-tab in Phase 1; planned default-on in
    Phase 2 after validating in the wild.

- packages/opencode/src/session/processor.ts:
  - Yield MCP.Service via Effect.serviceOption (doesn't leak through
    Handle.process's never-environment signature; processor still has
    MCP at runtime via prompt.ts's layer wiring)
  - case 'step-finish' (after value.reason captured, after summary fork):
    fires HivemindLoopHook.recordTurnEnd
  - case 'tool-result' (after completeToolCall): fires
    HivemindLoopHook.loopProgress

- packages/opencode/test/session/hivemind-loop-hook.test.ts (new):
  - 5 unit tests covering the gating paths:
    * flag off + MCP=None → silent no-op
    * flag on + MCP=None → silent no-op (no error)
    * flag off + (loopProgress) → silent no-op
    * flag on + MCP=None + (loopProgress) → silent no-op
    * flag on + MCP=Some (stub w/ empty clients()) → silent no-op (the
      findHivemindClient miss path)

- GRUNTCODE.md:
  - Documented as patch #5 with the feature flag + behavior.

Testing:
- bun typecheck (from packages/opencode): green.
- bun test test/session/ : 357 pass / 5 skip / 1 todo / 0 fail (was 352
  pre-hook; +5 new hook tests). No regressions across the session module
  (processor-effect.test.ts + compaction.test.ts + all peers in /session
  still green).
- The wake fire-path is exercised end-to-end by the production loop
  primitive once Phase 1 is enabled per-tab; that's better validated by
  live use than by mocked MCP calls in unit tests.

Refs:
- hivemind anomalyco#266 (parent — Phase 1)
- hivemind-mcp PR #18 (Phase 0 — schema + 6 tools)
- hivemind-mcp PR #20 (await-review decision branch — receiver-side already
  live on hivemind-mcp main as of 65a1020)
- AGENTS.md + hivemind-peers.md three-layer goal-loop contract (the
  behavioral rules this PR mechanically enforces)
terrxo added a commit that referenced this pull request May 28, 2026
… inbox (no more right-edge truncation) (#18)

Refs hivemind anomalyco#229. Nik caught visible truncation in screenshot — peer rows showing 'nik-t...' instead of 'nik-tab-2', summary lines clipped at card edge, inbox subject/body chopped to 'ack:' / 'CLAIM' fragments.

## Root cause

Sidebar interior width is ~38 cols (Sidebar is 42 wide, paddingLeft=2 + paddingRight=2). Opentui's <text> renderable clips horizontally by default — no auto-wrap on inline rows. Previous implementation crammed peer-id + summary on a single inline row which overflowed and got clipped at the right edge.

## Fix

Each peer / inbox row is now TWO explicit lines:

- **Peers**: line 1 = '· <peer-id> <relative-time>'. Line 2 = summary (wrapMode='word', truncated to 90 chars).
- **Inbox**: line 1 = '<sender>'. Line 2 = subject-or-body (wrapMode='word', truncated to 110 chars).
- **You section**: summary line gets wrapMode='word'; peer-id truncated to (SIDEBAR_INNER_COLS - 6) on overflow.

Plus a small truncate(s, max) helper that adds an ellipsis (…) so the eye sees that content was cut, not just disappeared.

Constants: SIDEBAR_INNER_COLS=38. Reflects the sidebar.tsx width=42 minus paddingLeft=2 + paddingRight=2.

## Verified

bun typecheck clean. Local install reports 1.15.10-grunt.7+local.0d4a394a8.dirty.
terrxo added a commit that referenced this pull request May 28, 2026
…alyco#266 Phase 1) (#21)

Bridges gruntcode's per-turn lifecycle to the hivemind-mcp loop primitive
shipped in hivemind-mcp v0.8.2 (PR #18) + extended in v0.8.3 (PR #20
await-review). Every step-finish fires hivemind_record_turn_end into the
MCP; every successful tool-result fires hivemind_loop_progress. The MCP
decides whether to auto-wake the same peer (continue), wake the parent
(escalate / await-review), or no-op. Workers self-drive toward goals
WITHOUT Nik typing.

This is Phase 1 of the loop primitive program (anomalyco#266). Phase 0 (schema +
tools + decision logic) lives in hivemind-mcp; this PR wires the gruntcode
side that emits the events.

Implementation:

- packages/opencode/src/session/hivemind-loop-hook.ts (new):
  - HivemindLoopHook.recordTurnEnd(...) — called from step-finish handler
  - HivemindLoopHook.loopProgress(...) — called from tool-result handler
  - Both take MCP.Service as Option (Effect.serviceOption) so the
    processor degrades cleanly when MCP isn't in context (tests don't
    have to provide MCP just to satisfy a single hook env requirement)
  - Both fire-and-forget via Effect.forkIn(scope) + Effect.ignore — the
    hook MUST NEVER block or throw into the TUI (loop primitive correctness
    rule; the loop is supposed to PREVENT failures, not cause them)
  - findHivemindClient: fast-path matches client named 'hivemind'; slow-
    path scans tools() map for the tool-name suffix (handles users who
    renamed their MCP under a non-conventional key)
  - buildTurnSummary deferred until AFTER client resolution to skip the
    Database.use read when we're no-opping anyway

- packages/opencode/src/effect/runtime-flags.ts:
  - New hivemindLoopEnabled flag (OPENCODE_HIVEMIND_LOOP_ENABLED env var,
    default false). Opt-in per-tab in Phase 1; planned default-on in
    Phase 2 after validating in the wild.

- packages/opencode/src/session/processor.ts:
  - Yield MCP.Service via Effect.serviceOption (doesn't leak through
    Handle.process's never-environment signature; processor still has
    MCP at runtime via prompt.ts's layer wiring)
  - case 'step-finish' (after value.reason captured, after summary fork):
    fires HivemindLoopHook.recordTurnEnd
  - case 'tool-result' (after completeToolCall): fires
    HivemindLoopHook.loopProgress

- packages/opencode/test/session/hivemind-loop-hook.test.ts (new):
  - 5 unit tests covering the gating paths:
    * flag off + MCP=None → silent no-op
    * flag on + MCP=None → silent no-op (no error)
    * flag off + (loopProgress) → silent no-op
    * flag on + MCP=None + (loopProgress) → silent no-op
    * flag on + MCP=Some (stub w/ empty clients()) → silent no-op (the
      findHivemindClient miss path)

- GRUNTCODE.md:
  - Documented as patch #5 with the feature flag + behavior.

Testing:
- bun typecheck (from packages/opencode): green.
- bun test test/session/ : 357 pass / 5 skip / 1 todo / 0 fail (was 352
  pre-hook; +5 new hook tests). No regressions across the session module
  (processor-effect.test.ts + compaction.test.ts + all peers in /session
  still green).
- The wake fire-path is exercised end-to-end by the production loop
  primitive once Phase 1 is enabled per-tab; that's better validated by
  live use than by mocked MCP calls in unit tests.

Refs:
- hivemind anomalyco#266 (parent — Phase 1)
- hivemind-mcp PR #18 (Phase 0 — schema + 6 tools)
- hivemind-mcp PR #20 (await-review decision branch — receiver-side already
  live on hivemind-mcp main as of 65a1020)
- AGENTS.md + hivemind-peers.md three-layer goal-loop contract (the
  behavioral rules this PR mechanically enforces)
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant