Skip to Content

Console

The console is a program section at /p/<slug>/console, not a fifth product. It appears in the rail when program.threads.read_own or program.threads.read passes and is used with program.threads.use. Its status word is designed; the roadmap names the gates that move it to preview. Pilot positioning, quoted from the contract: “non-GxP decision support with a light validation package”. The ELN stays the scientific record, the QMS stays the SOP record, Trovensa is the record of what agents proposed and what people decided.

Regions

RegionContentWidth at 1440 px and aboveRemembered in
Conversationmessages (role=feed), plan and approval cards, citation markers [n], composer with Stop and slash commands560 to 720 pxnothing
Canvascurrent artifact, version selector, selection-to-edit, promote, version diff, source viewer480 to 640 px, collapsiblecookie console_canvas
Run railsteps, tool calls (name, argument summary, scope, policy decision, permission_exercised, span id), sources read, budget used, SOP binding, model configuration record320 px, collapsiblecookie console_rail

Below 1100 px the canvas and rail become a tabbed drawer; below 768 px a bottom sheet with full-width cards. Mobile reads, approves, declines and stops; artifact editing is desktop-only and the button says why. Cookies hold layout only; everything else is a record.

Thread states: empty, planning, awaiting approval, running, paused (the composer prints “Paused, waiting for a decision”), finished, failed (with the public error code, for example gate_expired, policy_denied, config_mismatch), archived.

Threads and messages

RecordFields (DTO)Rule
Thread (ThreadOut)title, status (open, archived), visibility (private, program), created_by, scope_type, scope_id, product_slug, launch_context, pinned_context, schedule, last_message_at, message_count, redacted_atprivate by default; the owner reads it with program.threads.read_own; quality reviewers and leads read every thread with program.threads.read; only the owner shares, with a confirmation naming what becomes visible (ThreadShare.confirm_counts)
Message (MessageOut)seq, role (user, agent, system, tool), status (streaming, complete, failed), content_md, content_json, agent_run_id, author_user_id, parent_message_id, citation_count, claims_without_source, feedback (none, helpful, corrected, dismissed), feedback_by, feedback_notetext passes redaction before storage and never contains retrieved source text; citations reference document_chunks.id; feedback is rendered as labelled icons, not status chips
Agent run (AgentRunOut)phase-1 fields plus thread_id, mode (interactive, background), lane, agent_version_id, plan_hash, usage, trace_idone console_turn run per agent turn; runtime_session_id equals the run id
Interrupt (RunInterruptOut)seq, kind (plan_approval, write_approval, consent, question), payload, status, resolved_by, resolved_at, resolution (decision, reason, edits diff), permission_exercised, expires_atdefault expiry 24 h, extendable; resolution needs program.threads.use plus the write’s own permission
Tool call (ToolCallOut)specialist (from the run token), tool_key, arguments (redacted), proposal_hash, approved_hash, status, approved_by, approved_at, permission_exercised, resolution_reason, policy_decision, injection_flag, result_ref, otel_trace_id, otel_span_idwrites need an approved row whose approved_hash matches the payload the server writes
Snapshot (RunSnapshotOut)context (chunk ids and hashes, record ids, sop_version_ids, prompt version, allowlist, policy version, permission snapshot), context_hash, model_check (expected_model_config_id, reported_model_id_hash, inference_profile_id, outcome)every run is reproducible from the database; events stay excerpt-free

The composer shows the fixed phrase “Private to you. Quality reviewers and program leads can read this thread for review.” A record created from a thread links (thread_id, message_id, artifact_id, artifact_version, agent_run_id) and its page reads “From thread T-31” or “From a private thread” according to the reader’s rights. Conversations are redacted to tombstones after organizations.settings.thread_retention_months (default 24) while tool calls, interrupts, snapshots, signatures and audit rows persist (thread.redact).

Run modes and plan-first gates

Every run starts with a plan artifact (actions, tools, sources, expected writes, budget). The approved plan (agent_runs.plan_json, plan_hash) is the control flow: a tool outside it is refused with plan_violation and surfaces as a new interrupt. The mode is remembered per user in users.settings.console_run_mode (PATCH /me/settings):

Mode idLabelBehaviour
approve_each_writeRun and approve each writereads run; every write pauses the run for a decision
auto_reads_gated_writesRun with automatic reads and gated writesdefault; reads run within the plan, writes pause
keep_planningKeep planningthe plan is refined without running tools

Approval card

One component handles plan approval, write approval, connector consent and questions. It shows what will be written, which sources it cites, the downstream consequence and the permission exercised (disabled with the permission name when the viewer lacks it). Buttons: Approve (Enter), Edit then approve, Decline (Esc, the reason is stored as evaluation data); Resume re-opens an expired gate; Stop ends the run. Edits are applied by the server to the stored proposal, approved_hash is recomputed, the diff goes into resolution, and the record is written from the server-side payload only; the record page then reads “edited before approval” with the diff. A card that waits more than 15 minutes becomes a background run card and stays actionable. Approval is not a signature: signatures are reserved for approve routes and requirement-set acceptance (signatures).

Artifacts and canvas

Kinds are a fixed vocabulary rendered by UI-owned components: plan, comparison_table, finding_set, hypothesis_set, protocol_draft, report_draft, deviation_table, sop_diff, milestone_view, summary. Versions are append-only within a lineage_id; selection-to-edit yields v(n+1) with origin = agent, direct edits yield v(n+1) with origin = human and authored_by_user_id. Promote turns a version into a tool_calls proposal awaiting approval, never a direct write and never a visibility change. Items of kind observation need non-empty chunk_refs and cannot be promoted otherwise. Footer: “Agent-generated draft, unsigned.” followed by “Cites N sources.” and “Model configuration MC-n (recorded <date>)”.

Citations and citation coverage

Every factual sentence carries at least one [n] bound to (document_id, chunk_id, char_start, char_end). The source viewer shows title, provenance, licence code, retrieved_at, snapshot stamp, the cited span and “Open in notebook” or “Open at publisher”; rendering follows the licence codes. Citation coverage is a deterministic count of claim-shaped sentences without a marker (the pattern list lives in web/lib/content.ts claimPatterns); the header reads “All 9 claims carry a citation” or “1 claim without a citation”. It verifies markers, not that the chunk supports the claim; semantic grounding is a judged check in the evaluation suite. “Needs sources” withholds the write gate until citations are repaired or the user overrides with a note.

Record actions, palette and slash commands

Ctrl K gains Ask. On any record page . opens the record actions “Ask about this”, “Check against SOP”, “Prepare a draft from this”, “Compare with…”, “Send to QA” and “Accept into record”, filtered by the viewer’s permissions. The composer accepts /check, /draft, /compare, /brief and /pin. “Ask the agent” replaces the phase-1 agent action button; the run rail replaces the run dialog; legacy run kinds stay callable as canned prompts.

Keyboard and accessibility

role=feed composites for messages and rail; streamed text is announced through a polite role=log in sentence chunks; approval cards receive focus on appearance and return it on resolution; the sticky composer never obscures focus. Single-key shortcuts (a accept, . record actions, Enter, Esc) are active only with focus on the card or record header; users.settings.shortcuts remaps them to alt_chords or turns them off. Agent-origin content never uses the pass tone until accepted.

Notifications

notifications (bell and /me/notifications) fire on approval_requested, needs_input, run_finished, run_failed, sop_changed, batch_completed and connector_reauth. E-mail carries the record type, the human-set program name, the record id and a link only: no titles, no agent text. Reading a notification is audited (notification.read).

Performance budgets (design targets)

Optimistic echo under 100 ms; working indicator under 1 s (the state event is written in the message transaction); INP under 200 ms; SSE keepalive every 15 s; reconnect from Last-Event-ID within 2 s. First-token latency and the cold bound are measured and recorded in CLAIMS-REGISTER.md before any number appears in copy; none appears here.

Source: web/lib/content.ts (consoleSection, consoleCopy, runModes, recordActions, slashCommands, approvalActions, claimPatterns), web/lib/api/types.ts, web/lib/api/endpoints.ts, api/app/db/models/conversation.py, api/app/db/models/agent.py, api/app/auth/permissions.py, docs/lib/content.ts, PRODUCT-CONTRACT.md sections 10.1 to 10.4, PRODUCT-PLAN.md Part 4 section 3