Make the hive visible: deliberation UI, grammar installer, and an activity graph - #2166
Conversation
The openhuman subproject reference is advanced to a newer commit, incorporating upstream changes. No local modifications are involved. Auto-committed-on: macbook Co-authored-by: Medulla <medulla@tinyhumans.ai>
The grammar previously required a semicolon after every statement, which made it impossible to parse scripts that omit the final terminator. This change makes the semicolon optional at the end of a statement, so both terminated and unterminated statements are accepted. Auto-committed-on: macbook Co-authored-by: Medulla <medulla@tinyhumans.ai>
The hive grammar parse test file was previously removed but is now restored to ensure the parser continues to be validated against expected behavior. This re-adds the unit tests that verify the grammar correctly handles various input structures. Auto-committed-on: macbook Co-authored-by: Medulla <medulla@tinyhumans.ai>
The unit tests for hive grammar gating were previously removed and are now restored to ensure the feature remains covered. This change re-adds the test file to validate that grammar gating behaves as expected. Auto-committed-on: macbook Co-authored-by: Medulla <medulla@tinyhumans.ai>
The episode duration now falls back to the total duration when the episode-specific duration is unavailable, restoring the previous behavior that was accidentally removed. Auto-committed-on: macbook Co-authored-by: Medulla <medulla@tinyhumans.ai>
Adds unit tests for the hive episode fold functionality, covering the expected behavior of folding and unfolding episode data in the frontend. This ensures the feature works correctly and guards against regressions. Auto-committed-on: macbook Co-authored-by: Medulla <medulla@tinyhumans.ai>
The tone calculation previously returned an empty string when hive data was absent, which caused downstream consumers to receive an invalid tone value. This change restores the fallback to a default tone so that the UI remains consistent even when hive information is incomplete. Auto-committed-on: macbook Co-authored-by: Medulla <medulla@tinyhumans.ai>
The tone calculation previously returned an empty string when hive data was absent, which caused downstream consumers to receive an invalid tone value. This change restores the fallback to a default tone so that the UI remains consistent even when hive information is incomplete. Auto-committed-on: macbook Co-authored-by: Medulla <medulla@tinyhumans.ai>
The move chip component was referencing icon exports that no longer existed in the icons file, causing the chip to render without its directional arrows. This change re-adds the missing move icon definitions so the chip displays correctly again. Auto-committed-on: macbook Co-authored-by: Medulla <medulla@tinyhumans.ai>
The topic chip was inadvertently removed from verdict cards during a previous refactor. This change re-adds the chip component to display the associated topic for each verdict, ensuring users can see the topic context at a glance. Auto-committed-on: macbook Co-authored-by: Medulla <medulla@tinyhumans.ai>
The blind round band was previously hidden due to a conditional that checked for an undefined value, causing it to not render when the round data was present. This change corrects the condition to properly display the band during blind rounds, ensuring the visual indicator appears as intended in the standings rail. Auto-committed-on: macbook Co-authored-by: Medulla <medulla@tinyhumans.ai>
The color swatch labels in the styleguide view were accidentally removed during a previous refactor. This change restores them so that each color token is properly identified by name, making the styleguide useful again for designers and developers. Auto-committed-on: macbook Co-authored-by: Medulla <medulla@tinyhumans.ai>
The export function was omitting shot data due to an earlier refactor that removed the relevant block. This change re-adds the logic to include shot information in the exported output, ensuring the export matches the expected format. Auto-committed-on: macbook Co-authored-by: Medulla <medulla@tinyhumans.ai>
The shot view previously lost its data when the page was reloaded because the state was not being rehydrated from the URL. This change ensures the shot is reconstructed from the query parameters on load, so the view remains consistent after a refresh. Auto-committed-on: macbook Co-authored-by: Medulla <medulla@tinyhumans.ai>
The episode duration now falls back to the total duration when the episode-specific duration is unavailable, restoring the previous behavior that was accidentally removed. Auto-committed-on: macbook Co-authored-by: Medulla <medulla@tinyhumans.ai>
Adds unit tests for the hive episode fold functionality, covering the expected behavior of folding and unfolding episode data in the frontend. This ensures the feature works correctly and guards against regressions. Auto-committed-on: macbook Co-authored-by: Medulla <medulla@tinyhumans.ai>
The shot.mjs script was removed as it is no longer needed for capturing styleguide screenshots. This cleanup eliminates dead code that has been superseded by other tooling. Auto-committed-on: macbook Co-authored-by: Medulla <medulla@tinyhumans.ai>
The auto-commit hook picked up a submodule checkout that this branch never intended to move. Nothing in this work touches the vendored runtime. Co-authored-by: Medulla <medulla@tinyhumans.ai>
The model import was accidentally removed, breaking the chat view's ability to reference the model type. This restores the import so the view compiles and functions correctly again. Auto-committed-on: macbook Co-authored-by: Medulla <medulla@tinyhumans.ai>
The episode block was previously rendering an empty state due to a conditional that excluded valid content. This change corrects the logic so that episode details are displayed as intended, restoring the expected user-facing behavior. Auto-committed-on: macbook Co-authored-by: Medulla <medulla@tinyhumans.ai>
The message timeline was not displaying timestamps for messages, which made it difficult for users to see when each message was sent. This change re-adds the timestamp display to each message bubble, ensuring that the time of each message is visible again in the chat interface. Auto-committed-on: macbook Co-authored-by: Medulla <medulla@tinyhumans.ai>
The message timeline previously lost its automatic scroll-to-bottom behavior when new messages arrived, leaving users stranded mid-conversation. This change re-enables the scroll logic so the viewport follows the latest message as it is added, restoring the expected chat experience. Auto-committed-on: macbook Co-authored-by: Medulla <medulla@tinyhumans.ai>
The message timeline previously lost its automatic scrolling behavior when new messages arrived, leaving users stranded mid-conversation. This change re-enables the scroll-to-bottom effect so the viewport follows the latest message as it is added, restoring the expected chat behavior. Auto-committed-on: macbook Co-authored-by: Medulla <medulla@tinyhumans.ai>
The hover actions for chat messages were not appearing because the event handlers were attached to the wrong element. The handlers are now placed on the message container itself, ensuring the actions display correctly when hovering over any part of the message. Auto-committed-on: macbook Co-authored-by: Medulla <medulla@tinyhumans.ai>
The hover actions for chat messages were not appearing because the event handlers were attached to the wrong element. This change moves the mouse enter and leave listeners to the correct container so the action buttons display reliably when hovering over a message. Auto-committed-on: macbook Co-authored-by: Medulla <medulla@tinyhumans.ai>
The message input field was being cleared even when sending failed, causing users to lose their draft text. The input is now only cleared after a successful send, preserving the user's content for retry when an error occurs. Auto-committed-on: macbook Co-authored-by: Medulla <medulla@tinyhumans.ai>
The episode grouping logic was inadvertently removed during a previous refactor, causing consecutive episodes to be displayed as separate groups. This change restores the grouping behavior so that episodes with the same group identifier are merged into a single visual group, matching the expected user experience. Auto-committed-on: macbook Co-authored-by: Medulla <medulla@tinyhumans.ai>
The test file for chat episode grouping was previously removed, and this change restores it to ensure the grouping logic remains verified. The test validates that episodes are correctly grouped by their associated chat session. Auto-committed-on: macbook Co-authored-by: Medulla <medulla@tinyhumans.ai>
The color swatch labels in the styleguide view were accidentally removed during a previous refactor. This change restores them so that each color token is properly identified by name, making the styleguide useful again for designers and developers. Auto-committed-on: macbook Co-authored-by: Medulla <medulla@tinyhumans.ai>
The shot view previously lost its data when the page was reloaded because the state was only held in memory. This change rehydrates the shot from the URL parameters on load, so the displayed shot persists across refreshes. Auto-committed-on: macbook Co-authored-by: Medulla <medulla@tinyhumans.ai>
Changed the `from` field from "agent" to "company" in the test data for the late transcript entry to match the expected schema, ensuring the test accurately reflects the actual data structure used in production. Auto-committed-on: dragonfly
Use the broadly supported Object.prototype.hasOwnProperty.call to detect draft moves. This preserves move governance behavior in environments that lack Object.hasOwn. Auto-committed-on: dragonfly
Replace mutable default-initialized HiveConfig instances with struct literal syntax using the spread operator, making the test code more idiomatic and eliminating unnecessary mutability. Auto-committed-on: dragonfly
There was a problem hiding this comment.
💡 Codex Review
Here are some automated review suggestions for this pull request.
Reviewed commit: 91550047c3
ℹ️ About Codex in GitHub
Your team has set up Codex to review pull requests in this repo. Reviews are triggered when you
- Open a pull request for review
- Mark a draft as ready
- Comment "@codex review".
If Codex has suggestions, it will comment; otherwise it will react with 👍.
Codex can also answer questions or update the PR. Try commenting "@codex address that feedback".
The grammar panel now validates against the effective roster rather than the stale authored seats, preventing false validation errors when operators remove outdated entries. Additionally, marker line detection now skips indented code blocks, and the backend prioritizes an operator-installed grammar as the explicit override while retaining the older console-desk hive as a compatibility fallback for desks authored before the dedicated grammar overlay existed. Auto-committed-on: dragonfly
Lightweight room-test clients and older hosts do not expose the optional getDeskHive grammar read, so the fold now checks that the method exists before attempting to call it, retaining its derived policy in that case. Auto-committed-on: dragonfly
There was a problem hiding this comment.
💡 Codex Review
Here are some automated review suggestions for this pull request.
Reviewed commit: b19e9c707b
ℹ️ About Codex in GitHub
Your team has set up Codex to review pull requests in this repo. Reviews are triggered when you
- Open a pull request for review
- Mark a draft as ready
- Comment "@codex review".
If Codex has suggestions, it will comment; otherwise it will react with 👍.
Codex can also answer questions or update the PR. Try commenting "@codex address that feedback".
There was a problem hiding this comment.
The previously-blocking findings are resolved. Clearing the changes request.
$0.0782 · 885,177 in / 38,750 out · 582,948 cached (66%) · openrouter/openai/text-embedding-3-small, deepseek/deepseek-v4-flash, z-ai/glm-5.2 · 752 embedded
critique: $0.0047 · 65,503 in / 1,020 out · 0 cached (0%) · deepseek/deepseek-v4-flash
security: $0.0043 · 60,073 in / 961 out · 0 cached (0%) · deepseek/deepseek-v4-flash
tests: $0.0355 · 511,862 in / 27,758 out · 391,566 cached (76%) · z-ai/glm-5.2
description: $0.0337 · 247,739 in / 9,011 out · 191,382 cached (77%) · z-ai/glm-5.2
The episode parser now supports an alternative desk-settling report format that includes the settled agent and backing information in a different order, and the grammar panel avoids serializing a fully-restricted seat as an empty list to prevent unintended fail-open behavior at runtime. Auto-committed-on: dragonfly
The regex for parsing converged episode endings was updated to match a new format where the topic and turn count appear in a different order, with a period instead of a colon. The capture groups were reorganized and fallback logic added to extract the correct values from either the old or new format. Auto-committed-on: dragonfly
There was a problem hiding this comment.
Requesting changes: 1 lane(s) blocking, worst finding is high.
Fix or reply to the findings below and push. The next review clears this automatically once they are gone — you should not need to dismiss anything by hand.
$0.0735 · 618,023 in / 14,202 out · 407,487 cached (66%) · openrouter/openai/text-embedding-3-small, z-ai/glm-5.2, deepseek/deepseek-v4-flash · 752 embedded
critique: $0.0052 · 62,682 in / 1,230 out · 9,579 cached (15%) · z-ai/glm-5.2, deepseek/deepseek-v4-flash
security: $0.0045 · 49,938 in / 497 out · 8,830 cached (18%) · deepseek/deepseek-v4-flash, z-ai/glm-5.2
tests: $0.0324 · 257,538 in / 5,022 out · 193,686 cached (75%) · z-ai/glm-5.2
description: $0.0314 · 247,865 in / 7,453 out · 195,392 cached (79%) · z-ai/glm-5.2
| const turn: EpisodeTurn = { | ||
| messageId: message.id, | ||
| seq: seqOf(message), | ||
| agentId: message.channel ?? "", |
There was a problem hiding this comment.
Derive agentId from message.author rather than message.channel
Line 471 assigns message.channel ?? "" as the agentId for a non-referral turn. In the chat schema the channel field holds the desk/channel identifier, not the author's agent id — the author is typically the from or a distinct author field. Using the channel identifier as the agent id means a desk named "engineering" would produce agentId: "engineering" rather than the id of the teammate who wrote the message. This attribution error silently feeds into foldStandings, supporter counting, silencing, and the blind-round deduplication. The nearby referral handling correctly leaves agentId as the referral author constant; for a direct turn it should use whatever field the chat message stores the agent's own identifier under. If the chat schema puts the agent id in a different field than channel, this is a bug; if channel happens to be the correct source, the code should have a comment explaining the equivalence.
[RULE] incorrect-field-source ·
| setLoadError(null); | ||
| // A save/reset refusal from the desk this panel just left must not go on | ||
| // being shown under the desk it switched to. | ||
| setSaveError(null); |
There was a problem hiding this comment.
Clear busy on desk switch to avoid a stuck lock
When a save or reset is in flight and the operator switches desks, the promise's finally block is guarded by currentDeskId.current === issuedFor and will not call setBusy(false). The desk-change useEffect clears state, loadError, and saveError, but it does not clear busy. The panel therefore renders with busy === true permanently — every checkbox and both buttons stay disabled, and the operator has no way to recover short of a full reload. Add setBusy(false) to the desk-change effect.
[RULE] stuck-state-on-unmount ·
There was a problem hiding this comment.
💡 Codex Review
Here are some automated review suggestions for this pull request.
Reviewed commit: 51ec1671c2
ℹ️ About Codex in GitHub
Your team has set up Codex to review pull requests in this repo. Reviews are triggered when you
- Open a pull request for review
- Mark a draft as ready
- Comment "@codex review".
If Codex has suggestions, it will comment; otherwise it will react with 👍.
Codex can also answer questions or update the PR. Try commenting "@codex address that feedback".
|
|
||
| /// Build the payload for one desk. | ||
| fn desk_hive_dto(record: &crate::ports::CompanyRecord, desk_id: &str) -> DeskHiveDto { | ||
| let config = record.effective_desk_hive(desk_id); |
There was a problem hiding this comment.
Include legacy overlay desk hives in the DTO
For an operator-created desk carrying the older embedded OverlayDesk.hive, this accessor ignores that configuration and reports the default because it checks only the dedicated override and manifest. The runtime still deliberately reads the embedded value through effective_hive_config in src/hivemind/types.rs, so after a new override is reset the editor can show default quorum, moves, and referral settings while the next episode uses the legacy policy; build this DTO through the same effective-config ladder.
Useful? React with 👍 / 👎.
| const rows = [...candidate.turns, ...candidate.referrals, ...candidate.failed]; | ||
| const first = rows[0]?.at; | ||
| const reportAt = items.find( | ||
| (row) => row.kind === "message" && row.entry.message.id === candidate.reportId, | ||
| )?.at; | ||
| const last = reportAt ?? rows.at(-1)?.at; |
There was a problem hiding this comment.
Keep approvals ordered after an interleaved failed turn
Fresh evidence in the current approval-grouping fix is the running-episode fallback: these arrays are concatenated by category rather than timestamp, so when a failure note occurs before a later successful turn, rows.at(-1) is still the earlier failure. Until a closing report arrives, an approval raised between that failure and the later turn falls outside the episode block; the later turn is then appended to the already-emitted block, rendering the approval after work that happened later. Compute the interval from the minimum and maximum row timestamps.
Useful? React with 👍 / 👎.
| `enabled: true` cannot conjure a room out of one member, and a control | ||
| claiming otherwise would be a lie the host then quietly ignores. | ||
| */} | ||
| {seats.length < 2 ? ( |
There was a problem hiding this comment.
Count only live seats when offering room mode
When a grammar still names a departed seat and only one current member remains, seats contains both the live member and the intentionally visible stale repair row. This condition therefore offers an enabled “Answer as a room” switch even though the server derives deliberates: false from the one-member live roster, contradicting the explanatory invariant immediately above it. Use memberIds.length here while continuing to render stale rows in the matrix.
Useful? React with 👍 / 👎.
Update the pinned commits for the openhuman and tinyhivemind vendor dependencies to incorporate upstream changes. Auto-committed-on: dragonfly
There was a problem hiding this comment.
The previously-blocking findings are resolved. Clearing the changes request.
$0.0980 · 962,437 in / 51,716 out · 475,115 cached (49%) · openrouter/openai/text-embedding-3-small, deepseek/deepseek-v4-flash, z-ai/glm-5.2, minimax/minimax-m3 · 752 embedded
critique: $0.0099 · 104,807 in / 2,937 out · 19,466 cached (19%) · deepseek/deepseek-v4-flash, z-ai/glm-5.2
security: $0.0108 · 102,504 in / 3,537 out · 27,658 cached (27%) · deepseek/deepseek-v4-flash, z-ai/glm-5.2
tests: $0.0402 · 507,220 in / 36,838 out · 253,737 cached (50%) · minimax/minimax-m3
description: $0.0371 · 247,906 in / 8,404 out · 174,254 cached (70%) · z-ai/glm-5.2
| // orchestrator's recent-activity context and read by a model, so the | ||
| // same rule the workflow arms follow applies: no free text, no actor | ||
| // ids, and no configuration. | ||
| CompanyEvent::TeammateAdded { .. } => "teammate added".into(), |
There was a problem hiding this comment.
Inline desk_id and agent_id into the orchestrator context strings
The five new CompanyEvent arms in the orchestrator's recent-activity formatter drop the identifiers that would let a model tell which desk or teammate changed. The existing WorkflowDeleted arm immediately above follows the established format!("workflow deleted: {name} ({workflow_id})") pattern; the new arms do not.
The medulla arms in src/brain/medulla/effects.rs correctly carry the id and role (e.g. format!("@{by} added teammate {agent_id} ({role})")), but the orchestrator's own English summary is what the model reads when picking what to do next. As written, a model reviewing recent activity cannot tell which desk's membership changed, which teammate was added, which desk's move grammar was installed, or which desk was deleted — only that some count or shape changed somewhere. The TeammateAdded arm in particular drops every identifying field, even though the variant carries agent_id and role.
The fix is to mirror the WorkflowDeleted shape: destructure the id (and name/role where they exist) and inline them, so every sentence names the thing it talks about.
[RULE] missing-context ·
There was a problem hiding this comment.
💡 Codex Review
Here are some automated review suggestions for this pull request.
Reviewed commit: 339b69ea96
ℹ️ About Codex in GitHub
Your team has set up Codex to review pull requests in this repo. Reviews are triggered when you
- Open a pull request for review
- Mark a draft as ready
- Comment "@codex review".
If Codex has suggestions, it will comment; otherwise it will react with 👍.
Codex can also answer questions or update the PR. Try commenting "@codex address that feedback".
| const roomScopeKey = `${scope.connection}::${scope.company ?? "single"}`; | ||
| room.enterScope(roomScopeKey); |
There was a problem hiding this comment.
Include the client generation in the Room scope key
Fresh evidence after the earlier scope-binding fix is that connections/registry.ts::reseat replaces the OpenCompanyClient while deliberately preserving the connection id, but this key contains only that id and the company. After a host address or credential is reconfigured in place, enterScope therefore does nothing and writersForScope still accepts callbacks created for the old client, leaving the previous host's transcript/live state visible and allowing late writes into the replacement scope. Bind the store to the client identity or an explicit generation as scopeRef already does.
Useful? React with 👍 / 👎.
| foldEpisodes( | ||
| entries.map((entry) => entry.message), |
There was a problem hiding this comment.
Fold hive rows before ordinary thread filtering
Fresh evidence after the earlier threaded-row fix is this production call: entries comes from buildTimeline, which omits parented replies unless a single runtime reply can be promoted, while a real hive response always has multiple replies parented to its trigger (turns plus the closing report). Consequently this fold usually sees only the operator root and returns no episode; moreover the same filtered entries are passed to buildTimelineItems, so merely retaining parented rows inside foldEpisodes cannot create a visible block. Fold from the raw messages and ensure episode-owned replies are materialized before ordinary thread collapsing.
Useful? React with 👍 / 👎.
| const blindRows = rows.filter( | ||
| (row) => row.kind === "message" && blindIds.has(row.entry.message.id), | ||
| ); | ||
| const restRows = rows.filter((row) => !blindRows.includes(row)); |
There was a problem hiding this comment.
Preserve interleaved rows inside the blind round
Fresh evidence after the earlier approval-grouping fix is that this renderer repartitions the already chronological item.items: every blind turn is rendered first, followed by every other row. If an approval or failed-turn notice occurred between two opening-round turns, it is therefore moved after the entire blind round despite groupEpisodes assigning it at the correct timestamp, obscuring which turn raised it. Apply the blind styling without separating interleaved rows from their original sequence.
Useful? React with 👍 / 👎.
# Conflicts: # frontend/src/components/sidebar-controls.tsx # frontend/src/views/RoomView.tsx # frontend/src/views/chat/model.ts # frontend/src/views/company/CompanyView.tsx # frontend/src/views/room/BudgetDialog.tsx # frontend/src/views/team/DescribeTeammate.tsx # frontend/test/unit/chat-readonly-composer.test.ts # frontend/test/unit/cov-chat-budget-set-clear.test.ts # frontend/test/unit/cov-chat-members-budget-auth.test.ts # frontend/test/unit/live-frame-thread-key.test.ts # frontend/test/unit/team-add-one-box-chat.test.ts
There was a problem hiding this comment.
Requesting changes: 1 lane(s) blocking, worst finding is high.
Fix or reply to the findings below and push. The next review clears this automatically once they are gone — you should not need to dismiss anything by hand.
$0.0303 · 313,050 in / 14,495 out · 2,304 cached (1%) · openrouter/openai/text-embedding-3-small, deepseek/deepseek-v4-flash · 775 embedded
critique: $0.0227 · 230,699 in / 13,077 out · 2,048 cached (1%) · deepseek/deepseek-v4-flash
security: $0.0075 · 82,351 in / 1,418 out · 256 cached (0%) · deepseek/deepseek-v4-flash
| // What this line did in the room, when it was a turn in one. Absent | ||
| // for every ordinary reply, which is what keeps a single-responder | ||
| // desk rendering exactly as it always has. | ||
| turn={episodeTurn?.[item.entry.message.id]} |
There was a problem hiding this comment.
Add the turn prop to MessageRow before passing it
MessageRow does not accept a turn prop (as seen in its interface at frontend/src/views/chat/MessageRow.tsx:35-171). Passing it will cause a React unknown prop warning and the data will be ignored. The author must also add the turn prop to MessageRow's interface and use it for the component to function correctly.
[RULE] missing-prop ·
| // same shared approval card the Approvals page and the inline chat card use. | ||
| import { useAskerNames } from "@/components/approval-card"; | ||
| import type { DecidedApproval } from "@/views/chat/model"; | ||
| import type { DecidedApproval } from "@/views/room/model"; |
There was a problem hiding this comment.
Verify that the renamed import path resolves to a real file
The import path @/views/room/model is used only once in this diff, and no other context confirms that this file or the DecidedApproval type it exports actually exists. If the file doesn't exist or doesn't export DecidedApproval, the build will fail. This is a change that cannot be verified from the diff alone.
[RULE] broken-reference ·
| useEffect(() => { | ||
| transcriptsRef.current = transcripts; | ||
| }, [transcripts]); | ||
| const transcripts = room.useTranscripts(); |
There was a problem hiding this comment.
Add unit tests for room store hooks and writers
The diff replaces local useState for transcripts, hydration, chatChannelByThread, lastViewedChannel, unreadSince, openTurns, liveStepsByThread, liveStepsByMessage, and receiptByThread with hooks (room.use*) and writers from room.writersForScope. The PR adds focused tests for every behavior change according to the repo rules, but no test file for the new room store module is present in the diff. Every new module should have a dedicated test file; the store's enterScope, the scoped writers' idempotency, and each hook's subscription/unsubscription behavior need coverage.
[RULE] missing-test ·
There was a problem hiding this comment.
💡 Codex Review
opencompany/frontend/src/views/TeamView.tsx
Lines 448 to 452 in 2b99659
When an operator clicks this newly exposed Activity button, the current CompanyView has no comms branch or CommsView import; it treats every non-reserved company segment as a desk id and renders OrgChartView, so the activity graph is completely unreachable. Fresh evidence beyond the earlier observation-wiring comment is that the production rendering of CommsView itself was lost, not merely its observations prop.
opencompany/frontend/src/views/room/EpisodeBlock.tsx
Lines 125 to 130 in 2b99659
Following this ?hive destination renders only the desk's ordinary org chart: the current CompanyView neither reads the hive hash flag nor imports HiveGrammarPanel, and a repository-wide search finds the panel mounted only in the styleguide. Consequently the advertised GET/PUT/DELETE grammar editor has no production entry point, even when this URL is opened directly.
opencompany/frontend/src/lib/hive/episode.ts
Lines 425 to 426 in 2b99659
When two messages are submitted while the first deliberation is queued, accept_chat_turn journals both operator rows before either acquires the per-company serial lock, so this branch replaces the pending first trigger with the second. The first episode's subsequently journaled moves and report are then assigned to the second question, while the second episode becomes triggerless, even though every row's parentId identifies the correct trigger. Fresh evidence after the earlier segmentation fix is this queued-message ordering; use parentage rather than the latest operator row to select the open episode.
ℹ️ About Codex in GitHub
Your team has set up Codex to review pull requests in this repo. Reviews are triggered when you
- Open a pull request for review
- Mark a draft as ready
- Comment "@codex review".
If Codex has suggestions, it will comment; otherwise it will react with 👍.
Codex can also answer questions or update the PR. Try commenting "@codex address that feedback".
Make the hive visible
OpenCompany merged the hive-mind in #2097. A desk of two or more agents stopped
answering through a single responder and started deliberating —
!propose,!support,!object,!refute,!evidence,!question,!defer,!commit,!pin— until it converges, deadlocks, exhausts its turn budget or goes idle.None of it was visible.
grep -i hiveacrossfrontend/returned onlysubstrings of "archived". A deliberation rendered as a wall of chat bubbles
whose text happened to begin with
!propose. The most interesting thing theproduct does was invisible in the product.
This makes it visible, and closes the two gaps one level out: the company could
not draw itself talking to itself, and a grammar could only be installed by
editing
company.toml.What ships
The deliberation vocabulary (
frontend/src/lib/hive/) — the nine movekinds, topic standings, and episode folding as pure modules, designed against
fixtures on
#/styleguidebefore any route consumed them. Moves aredistinguished by shape, never by colour alone; a topic chip is a third form,
distinct from the identity tile and the status pill. Episode endings map onto
the existing closed five-status vocabulary (Converged →
done, Deadlocked →blocked, Exhausted/Idle →idle) rather than minting a new hue.Room renders a desk's answer as a room. The opening blind round is banded;
moves, topics,
^Ncitations and>Nobjection targets are drawn; standingsshow supporters, silenced advocates and refuters; the closing
hive-reportis averdict card rendered verbatim — the spec says the summary deliberately does
not restate the decision's content, so paraphrasing it would invent a claim.
A running room now has a rendering. It previously looked like a finished one
with fewer turns. It now reads "The desk is deliberating · turn 3 of 18 · 3
seats · budget derived", with
runningread offending === nullrather than alive flag — so it is still correct after a reload, when no frame is coming.
The grammar is installable —
GET/PUT/DELETE {scope}/desks/{id}/hive,with an editor at
#/company/<deskId>?hive. The manifest's own validator wasextracted and is now shared, so the runtime cannot accept a config the manifest
refuses.
An activity graph at
#/company/comms, plus five structuralCompanyEventvariants (
TeammateAdded,DeskCreated,DeskDeleted,DeskMembersChanged,DeskHiveConfigured) so "who created whom" is a durable fact rather than aninference from tool-call frames.
The room store (
frontend/src/room/store.ts) — nine fields lifted out ofthe 3955-line
app-shell.tsx, andviews/chat/renamed toviews/room/withits 1790-line
model.tssplit intochannels/timeline/review.Decisions worth reviewing
The rule that decides durable vs ephemeral. Journal a fact iff no durable
row already carries it; stream it iff it is a re-derivable view of something
already durable. Episode choreography is entirely re-derivable from the
transcript, so it adds zero
CompanyEvent— a second store of the episode isa second thing that can disagree with the fold, which is exactly what the driver
re-reading the journal every iteration exists to prevent. A delegation edge or a
minted teammate has no durable substrate at all, so those are journaled and add
no frames.
The live SSE half has no viewer projection.
company_eventsrunsproject_event_for_viewerover the durable half only; the ephemeral half mapsturn_stream::subscribestraight to SSE. So every hive frame is a pointer(carries
seq, console reads the text from theagent_replyprojection italready receives) and never content. A pointer frame cannot leak what the durable
projection would have withheld.
Room affordances are a data question, never a channel-kind question.
episodesFor()returns[]for every DM,#general, the Operator feed andevery single-responder desk, because none contains a marker line. There is no
conditional on
channel.kindand none ondesk.hive.enabled— a hive desk thatanswered in one turn renders as an ordinary conversation, which is the truth
about it.
A sibling
DeskHiveOverridecollection, not a field onOverlayDesk.OverlayDeskcovers only console-created desks, so the interesting case —installing a grammar on a manifest desk — would have needed a second
mechanism anyway. This follows the
AgentOverride/overlay_agent_editsprecedent. Replacement is wholesale, not field-wise: a
movestable is a singleartefact, and merging one seat into a stored table is how you get a grammar
nobody authored.
The composer offers no marker keyboard.
hivemind.md's gate table isexplicit that an operator message opens an episode and is not a turn in one, so
no marker in it is parsed as a move — a
!proposebutton would be a control thatdoes nothing. Recorded as a refusal in the README so the next reader finds the
reason rather than the absence.
Bugs found and fixed on the way
overlay_desk_hivewas dropped on the read path in bothstore/sqlite.rsand
store/mongodb.rs. The write side persisted correctly, so an installedgrammar would have vanished on every load — on MongoDB that is the hosted-tenant
path. Neither backend compiles under default features or
--features openhuman;--all-featuresis what catches it.delete_deskleaked a deleted desk's grammar, so an overlay deskre-created with the same id silently inherited a grammar nobody installed. Has
its own test.
foldStandingssilenced anadvocate across all topics, quietly turning every
!objectinto a!refute.Objection is local, refutation is global. Two regression tests; the fix is what
made the fold reproduce the host's own verdict.
Deliberate deviations from the plan
client-side (
^12→h12→ a row already intranscripts[channelId]), so thehive became visible without waiting on the refactor.
d3-forcenor@xyflow/react. Hand-drawn SVG:it is fed live and a force layout re-heats on every insertion, while xyflow's
canvas is more than a small layered graph needs. Hand-drawing also keeps every
colour on the design tokens.
a poll does the same job without threading shell state.
RoomStatusBarwas not built, though the plan called for it. Reading thecode refuted the premise:
ChatLiveReceipt/InflightRunBar/WorkingIndicatorare three placements across five call sites, not three variants of one row.
Merging them yields one component with three mount contexts and three shapes,
which is worse than what is there. The insight underneath it was real, and that
is what shipped — the running-room rendering above.
WorkHandedOffwas not added. It needs adelegatorthreaded throughDelegationRunnerat every construction site. The board'soriginChatId → assigneegives a desk-level edge meanwhile; the gap isdocumented in
frontend/src/views/comms/README.md.Also here: the desktop title bar
The shell went back to the native macOS title bar (
decorations: true, notitleBarStyle: "Overlay"). That had a tail worth naming: the frontend reserved72px for the floating traffic lights, and it decided to by asking "is this a mac
desktop?" — not by asking what the window config said. Config and layout agreed
only by coincidence. That inference is now a
SHELL_DRAWS_ITS_OWN_TITLE_BARconstant documented as having to match
tauri.conf.json, so the drag band andthe lights inset switch off together. The four affected tests were rewritten to
the new contract rather than deleted, and the components kept, so flipping the
constant back restores the whole arrangement.
The sidebar toggle became a 24px round FAB. Round because it straddles the
rail/content seam: a rounded square there has two edges running parallel to the
border a few pixels away and the eye joins them, so it read as a torn corner of
the rail. It is deliberately below the touch target — its mount site is
md:block, so it only exists on a pointer device.Verification
cargo fmt --all -- --checkcargo clippy --all-targets --features openhuman -- -D warningscargo clippy --all-targets --all-features(the gate that caught the store bug)cargo test --features openhuman— 7139 lib + 45 integration, 0 failedscripts/ci/assert-feature-lanes.sh,scripts/ci/assert-toolchain-pin.shtsc -b,typecheck:unit,typecheck:e2e,vite build, 4866 vitestdocs/design-system/color.mdreturns nothing new./scripts/desktop-dev.shOne flake seen and then not reproduced:
server::ops::workflows::tests::running::cancelling_an_unknown_or_settled_run_is_not_foundfailed once under full parallelism (
200where it wanted404), then passed inisolation and passed in a second full run. It settles a detached run and
immediately asserts the cancel route 404s, so the registry can still be holding
it. Pre-existing; this branch's only diff in that file is ten
overlay_desk_hive: Vec::new()fixture fills.Not done
Nothing here has been driven against a real deliberating desk. Everything is
verified against fixtures, unit tests and the scripted e2e. A live run needs
TINYHUMANS_API_KEYor the local ladder recipe indocs/plans/hivemind-handoff.md, againstcompanies/hive_math_lab— the onecompany with a real installed
movestable. Per that handoff doc,!objecthasnever appeared in a live run and cross-desk referral has tests but no live run at
all, so the UI for both is built against the spec alone and should be expected to
need revision.
The legacy chat surface is augmented, not replaced — the deliberation
rendering lives inside the renamed
views/room/. RewritingMessageTimeline/MessageRow/the composer and deletingRoomView.tsx(2779lines) remain: a big-bang rewrite of invariant-dense code with no flag, for
aesthetic gain, on a surface whose behaviour is now correct.
Worth confirming before this lands:
docs/spec/runtime/orchestration/delegation.mdis titled "Delegation, direction,and the end of desks" and describes collapsing desks into workflows. This invests
in desk-shaped UI. Flagged, not resolved.
Merge state
Conflicts with current
mainin one file,frontend/src/components/app-shell.tsx— expected, since the store extraction moved state out of it. The gates above ran
on the tree merged at the earlier
main; happy to re-merge and re-run.