Skip to content

Make the hive visible: deliberation UI, grammar installer, and an activity graph - #2166

Merged
senamakel merged 201 commits into
mainfrom
hive-console
Sep 10, 2026
Merged

Make the hive visible: deliberation UI, grammar installer, and an activity graph#2166
senamakel merged 201 commits into
mainfrom
hive-console

Conversation

@senamakel

Copy link
Copy Markdown
Member

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 hive across frontend/ returned only
substrings of "archived". A deliberation rendered as a wall of chat bubbles
whose text happened to begin with !propose. The most interesting thing the
product 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 move
kinds, topic standings, and episode folding as pure modules, designed against
fixtures on #/styleguide before any route consumed them. Moves are
distinguished 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, ^N citations and >N objection targets are drawn; standings
show supporters, silenced advocates and refuters; the closing hive-report is a
verdict 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 running read off ending === null rather than a
live flag — so it is still correct after a reload, when no frame is coming.

The grammar is installableGET/PUT/DELETE {scope}/desks/{id}/hive,
with an editor at #/company/<deskId>?hive. The manifest's own validator was
extracted and is now shared, so the runtime cannot accept a config the manifest
refuses.

An activity graph at #/company/comms, plus five structural CompanyEvent
variants (TeammateAdded, DeskCreated, DeskDeleted, DeskMembersChanged,
DeskHiveConfigured) so "who created whom" is a durable fact rather than an
inference from tool-call frames.

The room store (frontend/src/room/store.ts) — nine fields lifted out of
the 3955-line app-shell.tsx, and views/chat/ renamed to views/room/ with
its 1790-line model.ts split into channels/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 is
a 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_events runs
project_event_for_viewer over the durable half only; the ephemeral half maps
turn_stream::subscribe straight to SSE. So every hive frame is a pointer
(carries seq, console reads the text from the agent_reply projection it
already 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 and
every single-responder desk, because none contains a marker line. There is no
conditional on channel.kind and none on desk.hive.enabled — a hive desk that
answered in one turn renders as an ordinary conversation, which is the truth
about it.

A sibling DeskHiveOverride collection, not a field on OverlayDesk.
OverlayDesk covers 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_edits
precedent. Replacement is wholesale, not field-wise: a moves table is a single
artefact, 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 is
explicit 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 !propose button would be a control that
does 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_hive was dropped on the read path in both store/sqlite.rs
    and store/mongodb.rs. The write side persisted correctly, so an installed
    grammar would have vanished on every load — on MongoDB that is the hosted-tenant
    path. Neither backend compiles under default features or --features openhuman;
    --all-features is what catches it.
  • delete_desk leaked a deleted desk's grammar, so an overlay desk
    re-created with the same id silently inherited a grammar nobody installed. Has
    its own test.
  • An objection was being applied globally. foldStandings silenced an
    advocate across all topics, quietly turning every !object into 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

  • The episode UI shipped before the backend work. Citations resolve
    client-side (^12h12 → a row already in transcripts[channelId]), so the
    hive became visible without waiting on the refactor.
  • The comms graph uses neither d3-force nor @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.
  • The graph polls rather than subscribing. Once a frame only means "re-read",
    a poll does the same job without threading shell state.
  • RoomStatusBar was not built, though the plan called for it. Reading the
    code refuted the premise: ChatLiveReceipt/InflightRunBar/WorkingIndicator
    are 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.
  • WorkHandedOff was not added. It needs a delegator threaded through
    DelegationRunner at every construction site. The board's
    originChatId → assignee gives a desk-level edge meanwhile; the gap is
    documented 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, no
titleBarStyle: "Overlay"). That had a tail worth naming: the frontend reserved
72px 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_BAR
constant documented as having to match tauri.conf.json, so the drag band and
the 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 -- --check
  • cargo clippy --all-targets --features openhuman -- -D warnings
  • cargo clippy --all-targets --all-features (the gate that caught the store bug)
  • cargo test --features openhuman — 7139 lib + 45 integration, 0 failed
  • scripts/ci/assert-feature-lanes.sh, scripts/ci/assert-toolchain-pin.sh
  • frontend: tsc -b, typecheck:unit, typecheck:e2e, vite build, 4866 vitest
  • the colour grep in docs/design-system/color.md returns nothing new
  • desktop verified running via ./scripts/desktop-dev.sh

One flake seen and then not reproduced:
server::ops::workflows::tests::running::cancelling_an_unknown_or_settled_run_is_not_found
failed once under full parallelism (200 where it wanted 404), then passed in
isolation 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_KEY or the local ladder recipe in
docs/plans/hivemind-handoff.md, against companies/hive_math_lab — the one
company with a real installed moves table. Per that handoff doc, !object has
never 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/. Rewriting
MessageTimeline/MessageRow/the composer and deleting RoomView.tsx (2779
lines) 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.md is 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 main in 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.

senamakel and others added 30 commits September 7, 2026 02:27
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

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

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

💡 Codex Review

Here are some automated review suggestions for this pull request.

Reviewed commit: 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".

Comment thread src/server/operator.rs
Comment thread frontend/src/views/company/hive/HiveGrammarPanel.tsx
Comment thread frontend/src/lib/hive/grammar.ts
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

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

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

💡 Codex Review

Here are some automated review suggestions for this pull request.

Reviewed commit: 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".

Comment thread frontend/src/lib/hive/episode.ts Outdated
Comment thread frontend/src/views/company/hive/HiveGrammarPanel.tsx

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

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

Comment thread frontend/pnpm-workspace.yaml Outdated
@tinysweeper tinysweeper Bot added priority: p2 Soon. Real but survivable — a rough edge, a gap, a thing that will bite later. and removed priority: p1 Next. Wrong behaviour a user will hit, or a security weakness behind a condition. labels Sep 9, 2026
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

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

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 ?? "",

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

priority high critique likely

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);

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

priority medium tests confident

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 ·

@tinysweeper tinysweeper Bot added priority: p1 Next. Wrong behaviour a user will hit, or a security weakness behind a condition. and removed priority: p2 Soon. Real but survivable — a rough edge, a gap, a thing that will bite later. labels Sep 9, 2026

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

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

💡 Codex Review

Here are some automated review suggestions for this pull request.

Reviewed commit: 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".

Comment thread src/server/operator.rs

/// 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);

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

P2 Badge 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 👍 / 👎.

Comment on lines +778 to +783
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;

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

P2 Badge 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 ? (

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

P2 Badge 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

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

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(),

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

priority medium tests confident

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 ·

@tinysweeper tinysweeper Bot added priority: p2 Soon. Real but survivable — a rough edge, a gap, a thing that will bite later. and removed priority: p1 Next. Wrong behaviour a user will hit, or a security weakness behind a condition. labels Sep 9, 2026

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

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

💡 Codex Review

Here are some automated review suggestions for this pull request.

Reviewed commit: 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".

Comment on lines +487 to +488
const roomScopeKey = `${scope.connection}::${scope.company ?? "single"}`;
room.enterScope(roomScopeKey);

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

P1 Badge 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 👍 / 👎.

Comment thread frontend/src/views/RoomView.tsx Outdated
Comment on lines +1442 to +1443
foldEpisodes(
entries.map((entry) => entry.message),

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

P1 Badge 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 👍 / 👎.

Comment on lines +64 to +67
const blindRows = rows.filter(
(row) => row.kind === "message" && blindIds.has(row.entry.message.id),
);
const restRows = rows.filter((row) => !blindRows.includes(row));

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

P2 Badge 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
@senamakel
senamakel merged commit 9689841 into main Sep 10, 2026
5 of 13 checks passed

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

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]}

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

priority high critique confident

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";

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

priority high critique uncertain

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();

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

priority medium critique likely

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 ·

@tinysweeper tinysweeper Bot added priority: p1 Next. Wrong behaviour a user will hit, or a security weakness behind a condition. and removed priority: p2 Soon. Real but survivable — a rough edge, a gap, a thing that will bite later. labels Sep 10, 2026

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

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

💡 Codex Review

<Button
variant="outline"
render={<a href="#/company/comms" data-testid="company-activity" />}
>
<Network className="size-4" /> Activity

P1 Badge Route the Activity link to CommsView

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.


{deskId ? (
<a
href={`#/company/${encodeURIComponent(deskId)}?hive`}
className="ml-auto text-2xs text-muted-foreground underline decoration-dotted hover:text-foreground"
>
Move grammar

P1 Badge Wire the move-grammar destination into CompanyView

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.


if (open && open.turns.length > 0) close(null, null);
open = start(message);

P2 Badge Bind episode turns to their parent trigger

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

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

priority: p1 Next. Wrong behaviour a user will hit, or a security weakness behind a condition.

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant